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