mysql用insert ignore跳过唯一键冲突,postgresql用on conflict do nothing,sql server用merge配合when not matched then insert,跨库可用先查后插但有竞态风险。

MySQL 中用 INSERT IGNORE 跳过重复唯一键
当表有 UNIQUE 索引或 PRIMARY KEY,且你想插入一批数据但不想因重复报错中断,INSERT IGNORE 是最直接的解法。它会让 MySQL 对冲突行静默跳过,其余行照常插入。
常见错误现象:ERROR 1062 (23000): Duplicate entry 'xxx' for key 'PRIMARY' 或类似唯一键冲突提示,说明没加任何容错机制。
-
INSERT IGNORE INTO users (id, name) VALUES (1,'Alice'),(2,'Bob'),(1,'Charlie');—— 第二个(1,'Charlie')会被忽略,不报错也不插入 - 注意:它只忽略由
UNIQUE或PRIMARY KEY冲突引发的错误,其他错误(如字段长度超限、NULL 插入非空列)仍会报错并中止整个语句 - 返回值中
rows affected会少于实际 VALUES 数量,差值就是被忽略的行数,可用于判断有多少重复
PostgreSQL 中用 ON CONFLICT DO NOTHING 实现等效行为
PostgreSQL 没有 IGNORE,但 ON CONFLICT 子句更精准可控。只要指定冲突的目标约束(通常是唯一索引名或列名),就能明确告诉它“什么情况下跳过”。
使用场景:你建了 CREATE UNIQUE INDEX idx_users_email ON users(email);,插入时想按 email 去重。
INSERT INTO users (id, email) VALUES (1,'a@b.com'),(2,'c@d.com') ON CONFLICT (email) DO NOTHING;- 必须写明冲突列或约束名;如果写成
ON CONFLICT ON CONSTRAINT idx_users_email DO NOTHING,效果一样,但更显式、更安全(避免列名歧义) - 不能省略
DO NOTHING—— 缺少这部分语法不合法;也不能写成DO UPDATE SET ...如果你只想跳过
SQL Server 的 MERGE 语句不是“简单跳过”,但能达成同样目的
MERGE 在 SQL Server 中是标准方案,但它本质是“匹配+动作”逻辑,不是单条 INSERT 的变体。如果你只想要跳过,就得显式写 WHEN NOT MATCHED THEN INSERT,不写 WHEN MATCHED 分支。
容易踩的坑:初学者常误以为 MERGE 自带跳过逻辑,结果漏写 WHEN NOT MATCHED 导致全量失败或意外更新。
- 必须搭配
USING子句提供源数据,通常用VALUES构造行集:MERGE users AS t USING (VALUES (1,'Alice'),(2,'Bob')) AS s(id,name) ON t.id = s.id WHEN NOT MATCHED THEN INSERT (id,name) VALUES (s.id,s.name); - 目标表
users必须有唯一索引(如id主键),否则ON条件无法保证匹配唯一性,可能出错或行为不可预期 - 语句结尾必须有分号
;,SQL Server 对此要求严格,漏写会报语法错误
跨数据库通用方案:先 SELECT 再 INSERT(慎用)
如果无法控制方言(比如 ORM 封装太深,或需兼容多种数据库),可手动拆成两步:先查哪些主键/唯一值已存在,再对不存在的做批量 INSERT。但这会多一次查询,且在高并发下有竞态风险(查完到插之间别人插入了相同值)。
性能影响明显:1000 行插入若逐条判断,就是 1000 次 round-trip;即使批量查,也要额外执行一次 SELECT id FROM users WHERE id IN (...),再做集合差运算。
- 应用层伪代码:
existing_ids = db.query("SELECT id FROM users WHERE id IN %s", new_ids)→ 过滤出new_ids - existing_ids→ 批量插入剩余 ID - 仅推荐用于低频、小批量、或对原子性要求不高的场景;线上高频写入务必优先选原生数据库支持的方案
- 不要用
INSERT ... SELECT ... WHERE NOT EXISTS替代,那本质上还是MERGE思路,且多数数据库不支持直接对 VALUES 列表做这种嵌套
真正麻烦的不是语法本身,而是冲突判定粒度——比如你想按 (user_id, event_type) 联合唯一跳过,MySQL 和 PostgreSQL 都支持复合唯一索引,但写法上要确保 ON CONFLICT (user_id, event_type) 或 INSERT IGNORE 依赖的是对应索引,而不是只建了单列索引还指望它生效。











