首页/数据/SQL 速查/INSERT IGNORE 与 ON CONFLICT DO NOTHING
变更与事务52 条

INSERT IGNORE 与 ON CONFLICT DO NOTHING

跳过冲突行的插入:MySQL 用 INSERT IGNORE,PostgreSQL 用 ON CONFLICT DO NOTHING;前者还会把其它错误降级为警告。

SQL 速查数据更新于 2026-10-13

跳过冲突的插入

批量导入时经常遇到「这条已经存在,跳过就好」的需求。两种数据库都提供了对应写法,但容错范围差别很大:MySQL 的忽略模式会把一批可降级的错误全部转成警告,重复键、数据超长、类型不合法都在内,语句照样返回成功;PostgreSQL 的冲突处理只针对唯一约束一类冲突,其他错误仍然让整条语句失败。这个差别决定了它在数据质量把控上的定位完全不同。

基本信息

适用平台MySQL 8.0 / PostgreSQL 12 及以上
MySQL 写法在插入关键字后加忽略标记,冲突行跳过
PostgreSQL 写法冲突处理子句,可指定冲突目标后选择什么都不做
容错范围MySQL 会吞掉多类错误,PostgreSQL 只处理冲突
可观测性看受影响行数判断有多少行被跳过

典型使用场景

  • 维表初始化:把一批标签或分类先灌进去,已存在的不重复插入
  • 日志类写入:重复上报的数据直接跳过,不打断批处理
  • 数据修复脚本:补写缺失记录,已有记录保持原样
  • 采集任务入库:上游重复推送时保证幂等
  • 对比两种写法在严格环境下的行为差异

两库写法对照

两库写法对照
-- MySQL:冲突行跳过,但超长截断等错误也会变成警告继续执行
INSERT IGNORE INTO tags (id, name) VALUES (1, 'sql');

-- 执行后检查受影响行数:为零说明全部被跳过
SELECT ROW_COUNT();

-- PostgreSQL:只处理指定冲突目标,其他错误照常失败
INSERT INTO tags (id, name) VALUES (1, 'sql')
ON CONFLICT (id) DO NOTHING;

-- 不指定冲突目标时覆盖所有唯一约束
INSERT INTO tags (id, name) VALUES (1, 'sql')
ON CONFLICT DO NOTHING;

-- 想确认到底写入了哪些行,用返回子句观察
-- INSERT ... ON CONFLICT (id) DO NOTHING RETURNING id;

行为差异

情况MySQL 忽略模式PostgreSQL 冲突处理
重复键跳过该行,继续执行跳过该行,继续执行
数据超长转成警告并使用截断值语句失败
类型或取值不合法部分情况转成警告语句失败
影响行数返回实际写入行数返回实际写入行数
可指定范围不区分,全部忽略可指定冲突目标,范围可控
写批量导入脚本时把受影响行数与输入行数对比,是发现「被静默跳过」的最简单手段。差值持续增大说明上游数据与目标表已经不一致,需要单独处理。
需要「存在就更新」而不是「存在就跳过」时,两种数据库都提供冲突更新写法,此时应改用那一类语句,跳过式写法不会带来任何更新。

易错点

  • MySQL 的忽略模式会把超长截断、类型不匹配等错误一起吞掉,数据质量问题被掩盖,事后极难定位
  • 用跳过式写法实现「存在即跳过」,业务上错过的更新不会被发现,指标长期偏低
  • 批量导入后不看影响行数,跳过多少行完全不知情
  • 在严格数据质量要求的场景使用跳过式写法,脏数据静默入库
  • 以为 PostgreSQL 的冲突处理也能容忍其他错误:非冲突类错误照样让语句失败

数据来源与参考

  • MySQL 8.0 Reference Manual · INSERT IGNORE
  • PostgreSQL Documentation · INSERT
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌