对账系统中最易出错的是JOIN使用不当:LEFT JOIN用于单向差异(源有目标无),需ON全业务键+WHERE t2.id IS NULL;MySQL不支持FULL OUTER JOIN,须用LEFT+RIGHT JOIN UNION ALL替代;比对字段要严谨处理NULL和空字符串;性能依赖索引与范围过滤。

对账系统里最常卡住的不是逻辑,而是 JOIN 写错导致漏数据或重复——LEFT JOIN 和 FULL OUTER JOIN 选错、ON 条件漏写业务键、NULL 判断不严谨,三者任一出问题,差异结果就不可信。
用 LEFT JOIN 找「源系统有但目标系统无」的差异行
这是对账最基础的单向差异提取。关键不是“怎么连”,而是“连完怎么筛”:JOIN 后目标表字段为 NULL 才算真缺失,不能只看主键是否匹配。
-
ON条件必须包含全部业务唯一键(比如order_id+currency+settle_date),漏一个就可能把本该匹配的记录判成差异 - WHERE 子句必须显式写
t2.id IS NULL(假设t2是目标表别名),不能写t2.id = NULL—— 后者永远不成立 - 如果目标表存在多条相同业务键的脏数据,
LEFT JOIN会生成笛卡尔积,得先在子查询里GROUP BY或DISTINCT去重
SELECT t1.* FROM source_table t1 LEFT JOIN target_table t2 ON t1.order_id = t2.order_id AND t1.currency = t2.currency AND t1.settle_date = t2.settle_date WHERE t2.order_id IS NULL;
用 FULL OUTER JOIN 找双向差异(MySQL 用户绕开)
FULL OUTER JOIN 是找“两边都不全”的黄金操作,但 MySQL 直到 8.0.24 才支持,且多数生产环境仍是 5.7 或 8.0.22 以下。硬要用就得拼 LEFT JOIN + RIGHT JOIN + UNION ALL。
- MySQL 替代写法本质是:先取左缺右,再取右缺左,
UNION ALL合并(不用UNION,避免去重开销) - 注意两部分 SELECT 的字段顺序和类型必须严格一致,否则
UNION ALL报错 - Oracle/PostgreSQL 直接用
FULL OUTER JOIN更安全,但 WHERE 条件要写成t1.id IS NULL OR t2.id IS NULL,不能漏掉任一方向
-- MySQL 兼容写法 SELECT 'source_only' as diff_type, t1.* FROM source_table t1 LEFT JOIN target_table t2 ON t1.key = t2.key WHERE t2.key IS NULL UNION ALL SELECT 'target_only' as diff_type, t2.* FROM target_table t2 LEFT JOIN source_table t1 ON t2.key = t1.key WHERE t1.key IS NULL;
JOIN 后比对金额/状态等字段时,NULL 和空字符串要分开处理
对账不是只看“有没有”,更要核“对不对”。但 amount 字段经常是 NULL、0、空字符串混用,直接 t1.amount != t2.amount 会漏掉所有含 NULL 的行。
- 数值型字段优先用
COALESCE(t1.amount, 0) != COALESCE(t2.amount, 0),避免 NULL 参与比较 - 字符串字段慎用
COALESCE,因为''和NULL语义不同;建议用(t1.status IS NULL) != (t2.status IS NULL) OR t1.status != t2.status - 如果业务允许金额四舍五入误差,记得加
ABS(t1.amount - t2.amount) > 0.01,而不是简单不等号
性能卡点:JOIN 字段没索引 or 数据量大时临时表爆炸
对账表动辄千万级,JOIN 没走索引,或者 FULL OUTER JOIN 在内存不足时 spill to disk,查十分钟不出结果很常见。
- 确保
ON中所有字段在各自表上都有联合索引,顺序要和 JOIN 条件顺序一致(如INDEX(order_id, currency, settle_date)) - 大表对账前先用
WHERE过滤时间范围(比如WHERE settle_date BETWEEN '2024-01-01' AND '2024-01-31'),别让 JOIN 扫全表 - 如果仍慢,把中间结果存成临时表,并在临时表上建索引,比反复扫描原表快得多
真正难的从来不是写出 JOIN 语句,而是确认 ON 条件覆盖了所有业务唯一性约束、每个 NULL 都被显式处理、每张表的数据质量真实可信——这些没法靠 SQL 自动校验,得靠对账前的数据探查和样本验证。

















