NULL = NULL 在 ON 条件中不成立是 FULL JOIN 匹配异常的根本原因,因三值逻辑中返回 UNKNOWN 而非 TRUE,导致 NULL 行无法连接,各自独立出现在结果中,造成业务误判。

NULL = NULL 在 ON 条件里永远不成立
这是 FULL JOIN 匹配异常最根本的原因。SQL 的三值逻辑中,NULL = NULL 返回 UNKNOWN,不是 TRUE,所以不会触发任何连接行为——哪怕左表和右表都有 work_type IS NULL 的记录,它们也不会被连到一起。
结果就是:这些本该“对应”的 NULL 行,在结果集中各自独立存在,左表那行右表字段全为 NULL,右表那行左表字段全为 NULL。业务上可能是同一类未标注数据,报表一汇总却误判成“两边各丢了 N 条”。
验证方法:
SELECT COUNT(*) FROM table_a WHERE join_col IS NULLSELECT COUNT(*) FROM table_b WHERE join_col IS NULL- 再查
SELECT COUNT(*) FROM (a FULL JOIN b ON a.join_col = b.join_col) WHERE a.join_col IS NULL AND b.join_col IS NULL—— 正常应为 0
想让 NULL 之间也能匹配,得换判断方式
标准 SQL 中,IS NOT DISTINCT FROM 是唯一语义正确的解法(PostgreSQL / SQL:2003 支持),它把 NULL 视为相等值。但多数数据库不支持,所以常用退化写法:
- 用
COALESCE(a.join_col, -999) = COALESCE(b.join_col, -999),前提是-999在业务中绝不会出现 - 避免用字符串占位符(如
'<null>'</null>),防止类型隐式转换失败 - 如果字段是字符串且含空格,先
TRIM()再COALESCE,否则COALESCE(' ', '')还是不等
WHERE 里过滤右表字段会让 FULL JOIN 彻底失效
写 SELECT * FROM a FULL JOIN b ON a.id = b.id WHERE b.status = 'active',表面是“查全量再筛”,实际执行时,b.status = 'active' 会把所有 b.status IS NULL 的行(即左表有、右表无匹配的行)全部干掉——FULL JOIN 当场退化为 INNER JOIN。
正确做法只有两个:
- 把条件塞进
ON:例如ON a.id = b.id AND b.status = 'active' - 若真需在
WHERE筛,必须显式允许 NULL:WHERE b.status = 'active' OR b.status IS NULL,但要确认业务是否接受这种逻辑
MySQL 根本不支持 FULL JOIN,别白费劲
MySQL 所有官方版本(5.7 到 8.0.32)都明确不实现 FULL JOIN,词法解析阶段就报错 ERROR 1064 (42000): You have an error in your SQL syntax。这不是兼容性开关能打开的,也不是你少写了括号的问题。
替代方案只能手动拼:
LEFT JOIN ... UNION ALL SELECT * FROM b WHERE NOT EXISTS (SELECT 1 FROM a WHERE a.id = b.id)- 注意字段对齐、类型声明,以及当连接字段本身可为
NULL时,NOT EXISTS会漏数据——此时也得靠COALESCE兜底
真正难处理的不是 NULL 本身,而是它掩盖了“谁该为缺失负责”。保留原始 NULL 并加 source_flag 列,比盲目 COALESCE 更利于后续归因。

















