join旧表前必须清洗关联字段的null和空值,用coalesce或nullif统一语义,显式转换类型避免隐式转换导致精度丢失或索引失效,并将过滤条件置于on而非where以保障left join逻辑正确。

直接用旧表字段 JOIN 新表会因结构差异、空值或类型不一致导致结果错乱甚至报错;必须在连接前做字段对齐、NULL 处理和类型显式转换,而不是依赖隐式行为。
JOIN 旧表前必须清洗关联字段的 NULL 和空值
旧表常含大量 NULL、空字符串或占位符(如 'N/A'、'0'),若直接用于 ON 条件,会导致匹配失败或意外笛卡尔积。
- 用
COALESCE(col, '')或NULLIF(col, 'N/A')统一空值语义,但注意:加函数会使索引失效,仅限小表或预处理后使用 - 若旧表主键是
VARCHAR但新表是BIGINT,不能只靠ON old.id = new.id——MySQL 会隐式转为DOUBLE,引发精度丢失(比如'111111111111111111'和111111111111111110被判相等) - 推荐写法:
ON CAST(old.id AS CHAR) = CAST(new.id AS CHAR)或更安全地ON old.id = CAST(new.id AS VARCHAR(32)),确保两侧类型严格一致
LEFT JOIN 补全旧数据时,过滤条件必须写在 ON 里
业务上线后常需把旧表字段“补”进新表结构,例如用 old.status 填充新表的 status_v2 字段。此时若把状态过滤写在 WHERE,会把没匹配上的旧记录全丢掉。
- 错误写法:
SELECT * FROM new_table n LEFT JOIN old_table o ON n.ref_id = o.id WHERE o.status = 'active'→ 实际变成内连接,n中无对应o的行被剔除 - 正确写法:
LEFT JOIN old_table o ON n.ref_id = o.id AND o.status = 'active'→n全部保留,只取符合条件的o行或NULL - 若需排除已迁移的记录,应单独查缺失:
WHERE o.id IS NULL,且该WHERE必须是整个查询最后的过滤条件,不与其他逻辑混用
跨引擎或跨库 JOIN 旧表时,索引基本失效,得换策略
老系统常用 MyISAM 表或独立数据库实例,而新服务跑在 InnoDB 或另一套 MySQL 实例上。此时直接 JOIN 不仅慢,还可能返回不一致快照。
-
EXPLAIN看到被驱动表的type是ALL或index?说明没走索引——跨引擎不支持索引下推,别挣扎 - 临时方案:把旧表导出为 CSV,再
LOAD DATA INFILE导入新库的临时InnoDB表,再 JOIN - 长期方案:用 Flink CDC 或 Canal 同步旧表变更到新库宽表,避免运行时 JOIN;或者改用应用层聚合(如 Java 中先查新表 ID 列表,再批量
IN查旧表)
最易被忽略的是类型隐式转换带来的静默偏差——它不报错,但结果每天差几条,直到某次对账才发现订单金额对不上。务必在第一次 JOIN 前,用 COUNT(*) 和 COUNT(DISTINCT join_key) 对比旧表和新表的关联字段分布,确认是否真能一对一映射。











