mysql中用insert ignore可跳过主键或唯一索引冲突并静默忽略,适合“只插入不报错”场景;它不处理字段超长、null插入非空列等错误,且必须预先建好对应唯一约束,否则无效。

MySQL 中用 INSERT IGNORE 避免重复插入
当表有主键或唯一索引时,INSERT IGNORE 是最直接的方案:遇到重复冲突就跳过,不报错也不插入。它适合“只管插入、失败无所谓”的场景。
常见错误现象:ERROR 1062 (23000): Duplicate entry 'xxx' for key 'PRIMARY' —— 这说明你没加任何防重逻辑,直接硬插。
-
INSERT IGNORE INTO users (id, name) VALUES (1, 'Alice');:如果id=1已存在,语句执行成功(影响行数为 0),不会中断后续 SQL - 注意:
IGNORE会静默吞掉所有冲突和部分警告(比如字段截断),不是只针对主键冲突;调试时建议先关掉sql_mode里的STRICT_TRANS_TABLES,否则某些警告也会变错误 - 性能上基本等价于普通
INSERT,但冲突越多,实际写入越少,返回的affected rows可能为 0 —— 别靠它判断“是否真插入了”,要用ROW_COUNT()或应用层查一遍
PostgreSQL 用 INSERT ... ON CONFLICT 实现 upsert
PostgreSQL 没有 IGNORE,必须显式声明冲突后的行为。ON CONFLICT 是标准且可控的方式,支持忽略、更新、甚至自定义逻辑。
使用场景:你想在冲突时更新某个字段(比如更新最后登录时间),而不是单纯跳过。
-
INSERT INTO users (id, name, last_login) VALUES (1, 'Alice', NOW()) ON CONFLICT (id) DO NOTHING;:冲突时什么也不做 -
ON CONFLICT (id) DO UPDATE SET last_login = EXCLUDED.last_login;:用EXCLUDED引用本次想插入但冲突的那行数据 - 括号里必须填冲突列名(如
(id)或(email)),不能写ON CONFLICT DO NOTHING—— 会报错there is no unique or exclusion constraint matching the ON CONFLICT specification - 如果冲突列是组合唯一索引(如
UNIQUE (user_id, category)),ON CONFLICT (user_id, category)必须完全匹配索引定义顺序
SQL 标准写法:先 SELECT 再 INSERT(慎用)
看似通用,实则有竞态风险 —— 两个并发请求同时查到“不存在”,然后都去插入,第二个会失败。除非加锁或串行化事务,否则不推荐。
容易踩的坑:很多人用 SELECT COUNT(*) FROM table WHERE x = ? 判断是否存在,再决定是否 INSERT,这在高并发下大概率出错。
- 如果非要用,至少把两句包进一个事务,并设隔离级别为
SERIALIZABLE(但 PostgreSQL 默认不启用,MySQL 的SERIALIZABLE开销大) - 更现实的做法是:用
INSERT ... ON CONFLICT(PG)或INSERT IGNORE(MySQL)代替,它们底层已做原子性处理 - SQLite 支持
INSERT OR IGNORE和INSERT OR REPLACE,后者会删旧行再插新行,注意触发器和外键级联行为可能被意外触发
INSERT ... SELECT + NOT EXISTS 组合(跨表/条件插入)
当你要从另一张表批量导入、且只插那些目标表里还没有的记录时,NOT EXISTS 比单条判断更高效,也规避了并发问题。
典型场景:同步日志表中未处理的订单 ID 到任务队列表,避免重复派发。
INSERT INTO tasks (order_id, status) SELECT o.id, 'pending' FROM orders o WHERE NOT EXISTS (SELECT 1 FROM tasks t WHERE t.order_id = o.id) AND o.created_at > '2024-01-01';- 注意子查询里
SELECT 1就够了,别写SELECT *,避免字段变化导致出错 - 确保
tasks.order_id上有索引,否则NOT EXISTS会全表扫描,大数据量时极慢 - 这条语句本身是原子的,没有竞态问题 —— 但如果你在循环里对每一行单独执行它,那就又回到并发风险的老路
INSERT IGNORE 在 MySQL 里会抑制所有警告,而 ON CONFLICT 在 PostgreSQL 中要求精确匹配唯一约束定义——写错列名或顺序,语句直接报错。











