insert into ... select 后 delete 未生效,需用事务包裹并确保 where 条件一致;防重复用 insert ignore 或 on duplicate key update;大表删除应分批加 limit 和索引;验证须抽样比对字段内容而非仅 count(*)。

INSERT INTO ... SELECT 之后 DELETE 没生效?检查事务和 WHERE 条件
直接组合 INSERT INTO ... SELECT 和 DELETE 很容易误删或漏迁,根本原因是两条语句默认不构成原子操作。如果你没显式开启事务,中间出错或并发写入会导致数据不一致。
常见错误现象:DELETE 执行了但没删掉任何行,或者新表里有重复数据。
- 务必用
BEGIN TRANSACTION/COMMIT包裹两步操作(MySQL/PostgreSQL/SQL Server 均支持) -
DELETE的WHERE条件必须和前面INSERT INTO ... SELECT的WHERE完全一致,否则会删多或删少 - 避免用
SELECT *迁移 —— 字段顺序、NULL 默认值、自增列都可能引发隐式失败
MySQL 中用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE 防重复插入
迁移过程中目标表已有部分数据,直接 INSERT INTO ... SELECT 会因主键/唯一键冲突报错:ERROR 1062 (23000): Duplicate entry。
这时候不能硬删再插,得控制插入行为:
- 用
INSERT IGNORE INTO ... SELECT:冲突时跳过整行,不报错也不插入 - 用
INSERT ... ON DUPLICATE KEY UPDATE id=id(空更新):冲突时只更新指定字段,适合需要保留旧值的场景 - 注意
IGNORE也会忽略其他错误(如截断警告),生产环境建议配合SHOW WARNINGS检查
大表迁移时 DELETE 卡住或锁表?改用分批 + LIMIT
一次性 DELETE FROM table WHERE ... 在百万级以上表上极易锁表、拖慢线上查询,甚至触发超时或 OOM。
正确做法是把删除切片执行:
- 先确认
WHERE条件字段有索引(否则LIMIT无效且扫描全表) - 用类似
DELETE FROM source_table WHERE id BETWEEN ? AND ? LIMIT 1000分批删 - 每次删除后加短暂停顿(如
SLEEP(0.1)),降低对 I/O 和锁的竞争 - PostgreSQL 可用
DELETE ... USING配合临时表做更安全的批量删;SQL Server 推荐TOP (1000)
迁移后验证行数不一致?别只看 COUNT(*)
SELECT COUNT(*) FROM old_table 和 COUNT(*) FROM new_table 相等,不代表数据真一致 —— NULL 处理、时区转换、字符集隐式转换都可能导致内容差异。
关键验证点:
- 对比最小/最大主键值:
MIN(id), MAX(id),确认范围没丢段 - 抽样校验几行完整字段(尤其含
TEXT、JSON、时间类型),用MD5(CONCAT(...))快速比对 - 如果源表有
UPDATE_TIME或版本字段,检查迁移后是否全部覆盖,还是只取了快照时刻的数据
真正麻烦的是跨库迁移(比如 MySQL → PostgreSQL):类型映射、默认值策略、序列初始化这些细节,一个没对齐,后续就只能靠日志反推哪一行坏了。










