insert ignore 能跳过重复主键或唯一键冲突,是因为它依赖 mysql 引擎在遇到 primary key 或 unique 约束冲突时静默忽略该行,而非先查后插;若无对应唯一约束,该语句退化为普通 insert 并可能报错。

INSERT IGNORE 为什么能跳过重复主键或唯一键冲突
当表有 PRIMARY KEY 或 UNIQUE 约束时,INSERT IGNORE 遇到重复记录会静默丢弃该行,不报错也不插入。它不是“判断存在再插入”,而是靠数据库引擎捕获约束冲突后直接跳过——所以必须确保目标字段上有对应索引,否则无效。
常见错误是没建唯一索引就用 INSERT IGNORE,结果照样报错或重复插入。比如对 email 字段去重,但忘了加 UNIQUE(email)。
-
INSERT IGNORE只跳过因PRIMARY KEY、UNIQUE冲突导致的错误,其他错误(如字段类型不符、NOT NULL 违反)仍会报错 - MySQL 8.0+ 中,
INSERT IGNORE对生成列或函数索引的冲突也生效,但需注意表达式是否确定性 - 执行后
ROW_COUNT()返回实际插入行数(含被忽略的不算),可用于判断是否有新数据写入
ON DUPLICATE KEY UPDATE 怎么安全地“存在则更新,不存在则插入”
如果不想丢弃重复数据,而是想合并字段(比如累加计数、更新时间戳),ON DUPLICATE KEY UPDATE 是更可控的选择。它依赖同样的唯一约束触发,但把冲突转为更新操作。
典型场景:用户登录统计表,按 user_id 唯一,每次登录要更新 last_login 并自增 login_count。
INSERT INTO login_stats (user_id, last_login, login_count) VALUES (123, NOW(), 1) ON DUPLICATE KEY UPDATE last_login = VALUES(last_login), login_count = login_count + 1;
-
VALUES(col)引用的是本次INSERT语句中对应列的值,不是表中当前值 - 不能在
UPDATE子句里引用未在INSERT中出现的列,否则报错Unknown column 'xxx' in 'field list' - 若同时有多个唯一键(如
UNIQUE(a,b)和UNIQUE(c)),任一冲突都会触发更新,需确认业务逻辑是否允许
REPLACE INTO 的行为陷阱:它真不是“INSERT OR IGNORE”
REPLACE INTO 看似是“不存在就插、存在就替换”,但底层是先 DELETE 再 INSERT。这意味着:自增 ID 会变化、外键关联可能中断、触发器会执行两次、有事务隔离风险。
比如表有 id INT AUTO_INCREMENT PRIMARY KEY 和 UNIQUE(email),用 REPLACE INTO 插入已有 email 的记录,会导致 id 重新分配,下游依赖该 ID 的代码可能出错。
- 如果只是想避免重复,优先用
INSERT IGNORE或ON DUPLICATE KEY UPDATE -
REPLACE INTO在高并发下可能引发间隙锁争用,尤其配合AUTO_INCREMENT时 - 某些 ORM(如 Django ORM)不原生支持
REPLACE,需手写 raw SQL,维护成本更高
PostgreSQL 怎么实现类似效果:UPSERT 是唯一正解
PostgreSQL 没有 IGNORE 或 REPLACE,统一用 INSERT ... ON CONFLICT(俗称 UPSERT)。语法更明确,但也更严格:必须指定冲突目标(索引名或列名)和处理动作。
例如跳过重复:
INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'a@example.com') ON CONFLICT (email) DO NOTHING;更新则写
DO UPDATE SET name = EXCLUDED.name。
-
ON CONFLICT必须指向一个明确的唯一约束或索引,不能只写列名而无上下文 -
EXCLUDED是关键字,代表本次 INSERT 尝试插入的那行数据,不是NEW或VALUES - 在分区表上使用时,冲突检测只在目标分区生效,跨分区重复不会被捕获
跨数据库迁移时最容易忽略这点:MySQL 的 INSERT IGNORE 在 PostgreSQL 里没有等价简写,必须显式写全 ON CONFLICT 子句,少一个括号或关键字就语法报错。











