FULL OUTER JOIN 不自动标识差异,需用 WHERE 筛选 NULL 键及字段不等、用 CASE 标注三类差异行;MySQL 需用 LEFT+RIGHT+UNION ALL 模拟;NULL 处理、数据标准化和索引至关重要。

FULL OUTER JOIN 本身不合并差异,它只是把两表所有行都拉出来——差异得靠你用 WHERE 和 CASE 主动筛、主动标。
为什么直接写 FULL OUTER JOIN 看不到差异
执行 FULL OUTER JOIN 后,结果里混着三类行:匹配成功的、左表独有、右表独有。如果你只写 SELECT * FROM a FULL OUTER JOIN b ON a.id = b.id,90% 是正常匹配行,差异被淹没。
- 差异行一定出现在连接键为
NULL的位置:a.id IS NULL(右表有、左表无)或b.id IS NULL(左表有、右表无) - 如果两表都有该
id,但业务字段(如status、amount)不同,这些行的连接键不为NULL,必须额外加条件判断,比如a.status != b.status -
= NULL永远不成立,必须用IS NULL;!=遇到NULL会返回UNKNOWN,建议改用NOT (a.status = b.status)或显式处理空值
PostgreSQL/SQL Server 中一次性标出三类差异
用 CASE 打标签 + COALESCE 统一主键 + WHERE 过滤掉完全一致的行:
SELECT
COALESCE(a.order_id, b.order_id) AS order_id,
a.status AS status_a,
b.status AS status_b,
CASE
WHEN a.order_id IS NULL THEN 'only_in_b'
WHEN b.order_id IS NULL THEN 'only_in_a'
WHEN a.status != b.status THEN 'status_mismatch'
ELSE 'consistent'
END AS diff_type
FROM table_a a
FULL OUTER JOIN table_b b ON a.order_id = b.order_id
WHERE a.order_id IS NULL OR b.order_id IS NULL OR a.status != b.status;- 务必在
WHERE中包含所有差异条件,否则diff_type = 'consistent'的行也会进来,干扰排查 - 若
status可为NULL,a.status != b.status不可靠,换成NOT (a.status = b.status AND a.status IS NOT NULL AND b.status IS NOT NULL) - 确保
order_id上有索引,否则大表 JOIN 会慢到超时甚至 OOM
MySQL 用户必须用 LEFT + RIGHT + UNION ALL 模拟
MySQL 直接报错 ERROR 1064: You have an error in your SQL syntax,不能写 FULL OUTER JOIN。等效写法要满足三个硬约束:
- 用
UNION ALL,不是UNION——后者会去重,而“左表独有”和“右表独有”的NULL行结构相同,会被误删 - 右表独有部分必须加
WHERE a.id IS NULL,否则RIGHT JOIN会把交集行重复拉一遍 - 两边
SELECT的字段数、顺序、类型必须严格一致;不能用*,得显式列出并补NULL占位,例如:NULL AS status_a
示例:
SELECT a.id, a.name, a.status AS status_a, NULL AS status_b FROM table_a a LEFT JOIN table_b b ON a.id = b.id UNION ALL SELECT b.id, b.name, NULL AS status_a, b.status AS status_b FROM table_a a RIGHT JOIN table_b b ON a.id = b.id WHERE a.id IS NULL;
容易被忽略的 NULL 陷阱和业务逻辑坑
真正卡住人的往往不是语法,而是数据本身:
- 连接键(如
user_id)若允许NULL,FULL OUTER JOIN会产生笛卡尔爆炸——两表各有 5 条user_id IS NULL记录,结果就是 25 行。安全做法是在ON子句里排除:ON a.user_id = b.user_id AND a.user_id IS NOT NULL AND b.user_id IS NOT NULL - 状态值不等价:A 表用
'paid',B 表用'success',直接!=比永远报“不一致”。得先建映射或用CASE WHEN a.status = 'paid' THEN 'success' ELSE a.status END标准化 - 时间字段带时区、金额单位不统一、空字符串
''和NULL混用——这些不会报错,但会让差异结果全错
最后提醒一句:FULL OUTER JOIN 的结果里,任何涉及单边字段的 WHERE 条件(比如 WHERE amount > 100)都会静默过滤掉另一表的独有行,因为那列是 NULL。真要查“金额超 100 或根本没订单”,得写成 WHERE amount > 100 OR amount IS NULL。

















