insert ignore静默跳过主键/唯一索引冲突,不报错不改数据;replace into先删后插,id重置、默认值覆盖;on duplicate key update才真正实现存在则更新,安全可控。

INSERT IGNORE 遇到主键冲突时静默跳过,不改数据也不报错
当表有 PRIMARY KEY 或 UNIQUE 约束,而你要插入的值已存在时,INSERT IGNORE 会直接跳过整条语句,返回 0 rows affected,不抛异常、不触发警告以外的任何反馈。
常见错误现象:执行后查不到新数据,但也没报错——其实是被忽略了。必须靠 mysql_affected_rows() 或 SQL 返回的受影响行数判断是否真插入成功。
- 只抑制唯一性冲突(主键/唯一索引重复),其他错误如字段超长、类型不匹配仍会报错
- 适合幂等写入场景:定时同步用户资料、补日志、批量导入时容忍少量重复
- 不会重置自增 ID,也不会触发
BEFORE DELETE或AFTER INSERT触发器
示例:INSERT IGNORE INTO users (username, email) VALUES ('alice', 'a@b.com'); 若 username 已存在,该语句无任何副作用。
REPLACE INTO 实质是“删+插”,ID 和默认值都会变
REPLACE INTO 不是更新,而是先 DELETE 原记录、再 INSERT 新记录。只要命中任意一个 PRIMARY KEY 或 UNIQUE 索引冲突,就会走这个流程。
常见错误现象:执行后发现自增 ID 跳了、created_at 时间戳重置、外键级联删除意外发生、触发器被执行两次。
- 原记录的自增 ID 被释放,新记录获得新 ID(除非显式指定 ID)
- 未在
VALUES中显式给出的字段,会被设为默认值(如NULL、空字符串、0) - 若表有多个唯一索引,一条
REPLACE可能匹配并删除多行(例如url和name都是唯一索引,值分别撞到不同旧记录) - 性能开销更大:一次冲突 = 1 次 delete + 1 次 insert,I/O 和锁时间翻倍
示例:REPLACE INTO users (id, username, email) VALUES (1, 'bob', 'b@b.com'); 若 id=1 存在,原记录彻底消失,新记录的 updated_at 和 created_at 全部重置。
ON DUPLICATE KEY UPDATE 才是真正安全的“存在则更新”方案
它才是真正语义清晰、行为可控的冲突处理方式:尝试插入,失败则只更新指定字段,其余字段保持原值不变,自增 ID 不变,不触发级联删除,也不重置默认值。
常见错误现象:误用 REPLACE INTO 导致时间戳、计数器、JSON 字段等关键业务字段被清空;或误用 INSERT IGNORE 导致“看似成功实则没写入”的静默失败。
-
VALUES(col)表示本次INSERT语句中该列的值,不是当前行旧值 - 只更新你明确列出的字段,其余字段完全不动(包括
NOT NULL DEFAULT的字段) - 自增列不会递增,除非你在
UPDATE子句里显式给它赋值 - 影响行数为 2 表示“插入失败 → 更新成功”,不是删+插,而是 MySQL 内部优化后的原子操作
示例:INSERT INTO users (username, email, status) VALUES ('carol', 'c@d.com', 'active') ON DUPLICATE KEY UPDATE email = VALUES(email), status = VALUES(status);
表没有主键或唯一索引时,三者行为完全一致
这是最容易被忽略的前提条件:INSERT IGNORE、REPLACE INTO、INSERT ... ON DUPLICATE KEY UPDATE 全部依赖唯一性约束来检测冲突。如果表既无 PRIMARY KEY 也无 UNIQUE 索引,它们就退化成普通 INSERT,全部允许重复插入。
所以上线前务必确认:目标表是否建了对应索引?是 UNIQUE(username) 还是 UNIQUE(email)?还是复合唯一索引?索引字段顺序和 INSERT 中的字段顺序无关,但必须能覆盖冲突判断所需字段。
一旦漏建索引,REPLACE INTO 就变成裸插,可能造成脏数据;INSERT IGNORE 则完全失去“去重”意义;ON DUPLICATE KEY UPDATE 直接报语法错误(ERROR 1062 不会出现,因为根本没触发冲突逻辑)。











