主键冲突时,优先用INSERT IGNORE跳过重复;需覆盖旧记录才用REPLACE INTO,但其“删+插”机制有触发器、自增重置等副作用;根本解决需调整现有主键值或改用UUID/复合主键,并校验唯一性。
导入时主键冲突:INSERT IGNORE 还是 REPLACE INTO?
直接 insert into ... select 两份含自增主键的数据,大概率触发 error 1062 (23000): duplicate entry 'x' for key 'primary'。这时候别急着删原表或改主键——先看冲突性质。
如果只是想“跳过重复”,用 INSERT IGNORE;如果新数据要覆盖旧记录(含非主键字段更新),用 REPLACE INTO。但注意:REPLACE INTO 实际是「删 + 插」,会触发 DELETE 触发器、重置自增计数器,还可能因外键约束失败。
-
INSERT IGNORE更安全,适合去重导入场景 -
REPLACE INTO有副作用,仅在明确需要覆盖时使用 - 两者都不改变已有主键值,所以无法解决“两份数据主键范围重叠”这个根本问题
主键偏移量调整:ALTER TABLE AUTO_INCREMENT 不够用
假设 A 表主键范围是 1–1000,B 表也是 1–1000,你不能只调高 B 表的 AUTO_INCREMENT 就完事——那只是影响后续插入,B 表已有的 1–1000 还是会撞。
真正要动的是 B 表现有记录的主键值。常用做法是批量加一个固定偏移量,比如 +10000:
UPDATE `table_b` SET `id` = `id` + 10000;
但必须满足两个前提:
- B 表主键字段没被其他表用作外键(否则关联全断)
- 所有依赖该主键的索引、触发器、应用逻辑能容忍新值(比如 URL 中硬编码了 id)
执行前务必备份:mysqldump -u user db table_b > table_b_backup.sql
合并前清洗主键:用 UUID 或复合主键替代自增整数
如果这两份数据未来还要持续接入,靠人工偏移迟早出错。更可持续的做法是从源头规避冲突。
方案一:用 UUID() 替代 INT AUTO_INCREMENT(MySQL 8.0+ 支持 UUID_TO_BIN() 提升索引效率);
方案二:构造复合主键,比如 (source_id, local_id),其中 source_id 标识数据来源('a' 或 'b'),local_id 保留原始自增值;
- 复合主键无需偏移,天然隔离
- 查询时需带
source_id条件,否则全表扫描风险上升 - 迁移成本高:涉及所有关联表、应用代码、ORM 映射
导入后校验主键唯一性:别只信 ROW COUNT
执行完 INSERT IGNORE 或偏移更新后,SELECT COUNT(*) 看总数对不上,不等于就成功了。真正要查的是有没有漏掉的隐性冲突。
最直接的方式是查合并后表中主键是否真唯一:
SELECT `id`, COUNT(*) FROM `merged_table` GROUP BY `id` HAVING COUNT(*) > 1;
如果返回结果,说明仍有重复——常见原因是偏移量算错、部分记录没更新到、或用了 INSERT IGNORE 却没意识到它跳过了本该修正的行。
还有个容易被忽略的点:时间戳、更新标记等字段如果也参与业务逻辑判断,它们的值是否合理?比如 B 表的 created_at 被批量更新后,是否意外覆盖了原始时间?










