PostgreSQL、SQL Server、Oracle(12c+)原生支持FULL OUTER JOIN,MySQL和SQLite不支持,需用LEFT+RIGHT JOIN加UNION ALL模拟,并显式标记来源、补NULL、避免去重。

用 FULL OUTER JOIN 找两表差异前,先确认数据库是否支持
PostgreSQL、SQL Server、Oracle(12c+)原生支持 FULL OUTER JOIN;MySQL 直接不认这个语法,硬写会报错 ERROR 1054 (42S22): Unknown column 'xxx' in 'on clause' 或更直白的 Unknown syntax。SQLite 也不支持。
实操建议:
- 执行
SELECT version();或查文档确认当前数据库类型和版本 - MySQL 用户别挣扎,改用
LEFT JOIN + RIGHT JOIN + UNION ALL模拟(见下一条) - 即使支持,某些旧版 PostgreSQL(如 9.0 之前)对
FULL OUTER JOIN的 NULL 处理有边界 case,建议至少用 9.5+
MySQL 怎么等效实现 FULL OUTER JOIN 查差异
核心思路是把“左表独有”和“右表独有”两部分拼起来,再加一列标记来源。不能只用 UNION,因为会去重,而对比差异时重复行本身可能就是问题。
实操建议:
- 用
UNION ALL而非UNION,避免隐式去重干扰结果 - 显式补
NULL占位字段,保证左右两边列数、类型一致,否则UNION ALL会报错ERROR 1222 (21000): The used SELECT statements have a different number of columns - 加
source字段区分:比如左表标'left_only',右表标'right_only',方便后续过滤
示例(对比 orders_old 和 orders_new):
SELECT 'left_only' AS source, id, amount, created_at FROM orders_old WHERE id NOT IN (SELECT id FROM orders_new) UNION ALL SELECT 'right_only' AS source, id, amount, created_at FROM orders_new WHERE id NOT IN (SELECT id FROM orders_old);
FULL OUTER JOIN 的 ON 条件必须严格对应业务主键
很多人写成 ON a.id = b.id AND a.status = b.status,以为能同时筛状态一致的差异——这是错的。FULL OUTER JOIN 的 ON 只控制“怎么连”,不是“怎么过滤”。加多余条件会导致本该显示为 NULL 的行被提前剔除,漏掉真实差异。
实操建议:
-
ON里只放用于关联的字段,通常是主键或唯一业务键(如order_id、user_email) - 状态、时间、金额等需比对的字段,一律放到
WHERE或SELECT中做后置判断 - 如果关联字段本身含 NULL(比如
parent_id可为空),FULL OUTER JOIN无法匹配这些 NULL 行,得额外用OR (a.parent_id IS NULL AND b.parent_id IS NULL)补充,但极易出错,建议先COALESCE(parent_id, -1)规范化
查差异时,WHERE 条件写在 JOIN 后还是子查询里?
写在 FULL OUTER JOIN 的 WHERE 子句里,会把某一边为 NULL 的行直接干掉,变成 INNER JOIN 效果。比如 WHERE a.amount != b.amount,当 b.amount 是 NULL(即右表缺失),整行被过滤,你根本看不到“左表有、右表无”这类关键差异。
实操建议:
- 所有涉及
NULL判断的逻辑,必须用IS NULL/IS NOT NULL显式写出,例如:WHERE a.id IS NULL OR b.id IS NULL OR a.amount != b.amount - 复杂比对逻辑(如金额误差容忍 ±0.01)建议封装进子查询或 CTE,避免
WHERE层级过深导致可读性崩溃 - 大数据量下,先用
WHERE过滤无关分区(如created_at > '2024-01-01')再做 JOIN,比 JOIN 完再 WHERE 快得多
实际跑的时候,最容易卡在 NULL 和空字符串混用、时间字段时区不一致、字符集导致的隐式转换这三处。没打日志前,别急着调优。










