MySQL用INSERT IGNORE跳过主键/唯一键冲突,PostgreSQL用INSERT...ON CONFLICT(id) DO NOTHING,SQL Server推荐MERGE语句;跨库无通用方案,应用层预查更可控。

MySQL 中用 INSERT IGNORE 跳过重复主键或唯一键冲突
当表有 PRIMARY KEY 或 UNIQUE 约束时,INSERT IGNORE 会让 MySQL 忽略导致冲突的整行插入,继续处理后续行。它不会报错,也不会回滚已成功插入的行。
常见错误现象:ERROR 1062 (23000): Duplicate entry 'xxx' for key 'PRIMARY' —— 这正是 INSERT IGNORE 要解决的问题。
- 只对违反
PRIMARY KEY、UNIQUE或NOT NULL(仅限严格模式下)的约束生效,外键冲突不被忽略 - 被忽略的行在返回结果中计入
Warnings,可用SHOW WARNINGS查看 - 如果某行因其他原因失败(如类型转换错误),
IGNORE同样跳过,但这类错误不易察觉,需额外校验 - 示例:
INSERT IGNORE INTO users (id, name) VALUES (1,'Alice'), (2,'Bob'), (1,'Charlie');
——第 3 行因id=1重复被静默丢弃
PostgreSQL 中用 INSERT ... ON CONFLICT DO NOTHING 实现等效行为
PostgreSQL 不支持 IGNORE,必须显式声明冲突策略。ON CONFLICT 是唯一可靠方式,且必须指定具体冲突目标(如索引名或列名)。
容易踩的坑:直接写 ON CONFLICT DO NOTHING 会报错,因为 PostgreSQL 要求明确“和哪个约束冲突”。
- 最常用写法是
ON CONFLICT (id) DO NOTHING,其中id是主键或唯一键列名 - 若冲突基于组合唯一索引,需写成
ON CONFLICT (col_a, col_b) DO NOTHING - 不能省略括号内的列或索引名;也不支持模糊匹配(比如不指定列就跳过所有冲突)
- 示例:
INSERT INTO users (id, name) VALUES (1,'Alice'), (2,'Bob'), (1,'Charlie') ON CONFLICT (id) DO NOTHING;
SQL Server 的 MERGE 语句是更灵活但更重的替代方案
MERGE 本身不是“跳过”,而是通过 WHEN NOT MATCHED THEN INSERT 实现条件插入,天然规避重复。但它语法复杂,且需要明确指定匹配条件。
为什么不用 INSERT ... WHERE NOT EXISTS?单条还行,批量时性能差(每行都查一次),而 MERGE 是原子性批量操作。
- 必须提供
USING子句构造源数据集,通常用VALUES或临时表 -
ON后的匹配条件必须覆盖全部唯一性判断逻辑,漏写会导致误插或误跳 - 注意:SQL Server 对
MERGE有已知并发问题(如重复插入),生产环境建议加HOLDLOCK提示 - 示例片段:
MERGE users AS t USING (VALUES (1,'Alice'),(2,'Bob'),(1,'Charlie')) AS s(id,name) ON t.id = s.id WHEN NOT MATCHED THEN INSERT (id,name) VALUES (s.id,s.name);
跨数据库兼容写法几乎不存在,应用层过滤成本可能更低
想写一条 SQL 在 MySQL/PG/SQL Server 上都跳过冲突?基本不可行。各数据库的语法、语义、甚至冲突判定粒度(如 NULL 是否算重复)都不一致。
真正值得考虑的是:是否真的需要数据库层“跳过”?有时候提前过滤更可控。
- 如果批量数据来自应用内存(如 Python 列表),先用
SELECT id FROM users WHERE id IN (...)预查已存在 ID,再剔除后插入,逻辑清晰且可审计 - 对超大批量(百万级),预查可能变慢,此时应按数据库选原生方案,而非强求统一
- 特别注意:所有“跳过”方案都会掩盖数据问题——比如你以为只是重复,实际可能是上游生成 ID 逻辑缺陷,日志里不报错反而更难定位

















