在 SQL 查询里,很多人第一次遇到“两个字段都是 NULL 却连不上”时,往往会以为是数据库行为异常。其实这正是 SQL 标准的默认规则:JOIN 判断条件时只认 TRUE,而 NULL = NULL 并不会得到 TRUE。
如果业务上确实要求“空值也要视为可匹配”,处理方式不能靠猜,而要看你用的是标准 SQL 兼容写法,还是数据库专属的 NULL 安全比较符。下面按原理、可用写法和风险点分开说明,便于直接套到实际查询里。
为什么 NULL = NULL 不能触发 JOIN
根本原因在于 SQL 使用三值逻辑。只要等值比较里出现 NULL,结果就不是 TRUE 或 FALSE,而是 UNKNOWN。而 JOIN ... ON 只会在条件结果为 TRUE 时建立关联。

因此,即使 a.id 和 b.id 同时为 NULL,下面这类写法也不会匹配成功:
ON a.id = b.id
这个规则在主流数据库里是一致的,包括 MySQL、PostgreSQL、SQL Server、Oracle,都没有例外。
INNER JOIN 和 LEFT JOIN 的结果差别
INNER JOIN:条件不成立的行会被直接丢弃。LEFT JOIN:左表行会保留,但右表字段全部显示为NULL。
这里要注意,LEFT JOIN 右侧出现 NULL,并不代表“两个 NULL 成功连上了”,而是右表根本没有匹配到记录。
最通用的写法:显式补上 NULL 匹配条件
如果你希望“值相等”或“双方都为 NULL”都算匹配,兼容性最好的方式就是把这两种情况都写进 ON 条件里。

ON (a.id = b.id) OR (a.id IS NULL AND b.id IS NULL)
这类写法适用于所有支持标准 SQL 的数据库,也是跨库场景下最稳妥的方案。
为什么括号不能省
虽然下面的写法在运算优先级上看起来结果一致,但可读性差,也更容易在后续追加条件时引入错误:
ON a.id = b.id OR a.id IS NULL AND b.id IS NULL
因为 AND 的优先级高于 OR,数据库会先计算 a.id IS NULL AND b.id IS NULL。对当前这个简单表达式来说,逻辑上确实等价于:
ON a.id = b.id OR (a.id IS NULL AND b.id IS NULL)
但在真实查询里,ON 条件通常不止一项。把括号写全,才能保证后续维护时不会误判逻辑边界。
多个字段关联时怎么写
如果 JOIN 条件涉及多个字段,就要对每一对字段分别补上“值相等或同时为 NULL”的判断,再用 AND 串起来:
ON (a.x = b.x OR (a.x IS NULL AND b.x IS NULL))
AND (a.y = b.y OR (a.y IS NULL AND b.y IS NULL))
不要把多个字段混成一个整体条件,否则很容易出现部分字段为 NULL 时误匹配的问题。
数据库专用简写:PostgreSQL 和 MySQL
如果你不追求跨数据库兼容性,可以使用数据库提供的 NULL 安全比较语法,表达会更短,也更贴近“相等或同时为 NULL”的真实语义。
PostgreSQL:IS NOT DISTINCT FROM
ON a.id IS NOT DISTINCT FROM b.id
这条语句的含义就是:两个值相同,或者两边都为 NULL,都算匹配。
MySQL:<=>
ON a.id <=> b.id
这里的 <=> 是 MySQL 的 NULL 安全等号,不是“小于等于加大于”的组合写法,也不要和 <= 混淆。
兼容性限制
- PostgreSQL 支持
IS NOT DISTINCT FROM - MySQL 支持
<=> - SQL Server 和 Oracle 不支持这两种语法,直接使用会报错
因此,只要 SQL 需要跨库迁移、共享到多套环境,仍然优先考虑显式 OR 写法。
为什么不建议用 COALESCE() 硬凑相等
不少人会把 NULL 先替换成一个兜底值,再做等值比较,例如:

COALESCE(a.x, -1) = COALESCE(b.x, -1)
这种写法表面上简洁,实际风险不小,尤其不适合作为通用 JOIN 方案。
风险 1:可能制造误匹配
它依赖一个“业务中绝不会出现”的特殊值。问题在于,这个前提很难长期成立。
COALESCE(user_id, -1)
如果后续数据里真的出现了 id = -1,原本不该关联的记录就会被错误连在一起。
风险 2:可能影响索引和性能
当 COALESCE() 直接作用在连接字段上时,数据库通常无法按原始列走索引,JOIN 性能可能明显下降。数据量一大,这类写法的代价会比语法简化带来的收益高得多。
风险 3:还会引出类型转换问题
如果 a.x 是字符串、b.x 是数字,COALESCE() 还可能触发隐式类型转换,带来报错、精度问题或意外匹配结果。
当你频繁需要“NULL 参与关联”时,先回头看数据建模
真正值得警惕的,往往不是 JOIN 语法本身,而是需求背后的数据语义。如果大量业务逻辑都依赖“NULL 和 NULL 应视为相等”,那通常说明字段设计没有把“未知”“不适用”“未填写”这些状态区分清楚。
例如,有些场景更适合用明确枚举值,比如 'unknown' 或 'not_applicable',而不是把所有含义都压进 NULL。与其在 SQL 层不断追加补丁,不如先统一字段规范,这样后续查询、索引设计和跨系统协作都会更稳定。
实际使用时怎么选
- 需要跨数据库兼容:优先用
ON (a.id = b.id) OR (a.id IS NULL AND b.id IS NULL) - 只跑 PostgreSQL:可用
IS NOT DISTINCT FROM简化 - 只跑 MySQL:可用
<=>简化 - 追求省事想用
COALESCE():除非你能确认兜底值、索引影响和类型行为都可控,否则不建议用于正式 JOIN 条件
一句话总结:SQL 不是不能让 NULL 参与关联,而是默认不会这么做。你需要明确告诉数据库,“值相等”之外,“双方都为 NULL”也算匹配。









