JOIN 迁移中 WHERE 与 ON 位置错误会导致数据丢失:WHERE 在连接后过滤会剔除 NULL 行,使 LEFT JOIN 退化为 INNER JOIN;补全用 ON 加条件,查缺失用 WHERE new.id IS NULL。

JOIN 迁移时 WHERE 和 ON 的位置决定数据是否丢失
用 JOIN 做迁移,最容易误删或漏迁数据的根源,是把过滤条件错放在 WHERE 而不是 ON。特别是用 LEFT JOIN 补全目标表时,WHERE 会把右表为 NULL 的行整个踢掉,等于变相转成了 INNER JOIN。
常见错误现象:LEFT JOIN 后加了 WHERE target.id IS NULL 想找未匹配记录,结果查不到——因为 WHERE 在连接后执行,此时左表没匹配的行已被 NULL 化,但又被 WHERE 当成“不满足条件”过滤掉了。
- 匹配补全场景(如:把旧表字段同步到新表)→ 过滤条件写在
ON子句里,例如ON old.id = new.id AND old.status = 'active' - 查找缺失记录(如:哪些旧数据还没迁入新表)→ 必须用
WHERE new.id IS NULL,且确保这个WHERE是在LEFT JOIN之后、无其他干扰条件的前提下单独使用 - 多表关联迁移时,每个
JOIN的过滤逻辑必须独立约束在对应ON中,避免跨表条件混进WHERE
UPDATE … FROM 与 INSERT … SELECT 中的 JOIN 写法差异
MySQL 不支持 UPDATE …… FROM,PostgreSQL 和 SQL Server 支持,但语法细节不同;而 INSERT …… SELECT 虽然通用,但字段顺序、类型隐式转换、重复键处理极易出错。
使用场景:批量更新目标表某字段,依据源表关联值;或插入一批经关联计算后的记录。
- PostgreSQL 更新示例:
UPDATE orders SET status = src.new_status FROM order_updates AS src WHERE orders.id = src.order_id—— 注意这里FROM后不能加JOIN关键字,直接跟表名 + 别名即可 - MySQL 替代方案只能用
UPDATE …… JOIN:UPDATE orders o JOIN order_updates u ON o.id = u.order_id SET o.status = u.new_status -
INSERT …… SELECT插入前务必检查字段数量和顺序是否与目标表严格一致,否则可能静默错位;若目标表有自增主键,记得在SELECT中跳过该列,或显式设为DEFAULT
JOIN 迁移中 NULL 值引发的隐式类型转换陷阱
当连接字段存在 NULL,尤其涉及字符串拼接、数值计算或日期比较时,整行可能被意外排除,或结果字段变成 NULL 而不报错。这不是 bug,是 SQL 标准行为,但常被忽略。
典型表现:迁移后发现某些记录的金额、时间戳字段全为空,查日志却没报错;或者 COUNT(*) 和 COUNT(connected_field) 差距极大。
- 连接字段必须提前清洗:
ON COALESCE(old.ref_id, '') = COALESCE(new.ref_id,''),但要注意这会影响索引使用 - 数值类字段做关联时,优先用
CAST(old.num_id AS BIGINT)统一类型,避免字符串型数字(如'00123')和整型123不等价 - 日期字段别直接
old.updated_at = new.updated_at,毫秒级差异会导致匹配失败;改用范围判断更稳妥:ABS(EXTRACT(EPOCH FROM (old.updated_at - new.updated_at)))
大表 JOIN 迁移卡死或超时的几个硬指标
单次迁移涉及千万级以上记录时,JOIN 不是慢,而是根本跑不完——执行计划崩坏、临时空间爆满、锁住整个源表都是常见后果。
性能影响核心在三处:连接字段是否有有效索引、驱动表选择是否合理、是否一次性加载全部结果。
- 确认
EXPLAIN输出中type是ref或eq_ref,而非ALL;如果出现Using join buffer,说明内存不够,得调大join_buffer_size(但治标不治本) - 小表驱动大表:把记录少的表放
FROM,大表放JOIN后;若两表都大,先用WHERE缩减驱动表数据量(如按时间分批) - 不要
SELECT *迁移,只选必要字段;插入时用INSERT INTO …… VALUES (……), (……), ……批量提交,每批控制在 5000 行内
最麻烦的其实是外键约束和触发器——它们会在每次 INSERT 或 UPDATE 时被激活,放大延迟。迁移前临时禁用(记得事后恢复),比优化 SQL 更立竿见影。