不能。SQL标准规定NULL = NULL返回UNKNOWN而非TRUE,而JOIN只保留ON条件为TRUE的行,故两边均为NULL也无法匹配;需用OR分支、COALESCE或IS NOT DISTINCT FROM实现NULL安全比较。

NULL值在SQL JOIN中为什么永远不等于NULL?
因为SQL标准规定,NULL = NULL 的结果不是 TRUE,而是 UNKNOWN,而JOIN(包括INNER/LEFT/RIGHT)只保留判断为 TRUE 的行。哪怕两列都是NULL,也不会被当作“相等”来关联。
这和编程语言里 null === null 返回 true 完全不同——SQL的NULL代表“未知值”,两个未知值之间无法确定是否相同。
常见错误现象:
- 两张表都有
user_id字段,其中部分值为NULL,执行INNER JOIN ON a.user_id = b.user_id后,这些NULL行全部消失 - 用
LEFT JOIN时,右表对应字段本应有数据,却因左表是NULL而没匹配上,导致右表字段全为NULL
如何让NULL与NULL也能JOIN上?
必须显式绕过三值逻辑(TRUE/FALSE/UNKNOWN),把NULL之间的比较转成布尔判断。最常用且兼容性最好的写法是:
ON (a.user_id = b.user_id) OR (a.user_id IS NULL AND b.user_id IS NULL)
注意:不能只写 a.user_id IS NULL AND b.user_id IS NULL,否则会漏掉非NULL的正常匹配。
其他可行但需谨慎的方式:
- 用
COALESCE(a.user_id, -1) = COALESCE(b.user_id, -1)—— 要确保替换值(如-1)在业务中不会真实存在 - 用
IS NOT DISTINCT FROM(PostgreSQL支持,MySQL 8.0.22+、SQL Server 2022+ 也支持)—— 语义最清晰:a.user_id IS NOT DISTINCT FROM b.user_id
LEFT JOIN + NULL关联时,ON和WHERE的区别很关键
如果在 LEFT JOIN 后加了 WHERE 过滤NULL字段,很容易误把本该保留的左表行过滤掉。
比如想查所有用户及其订单,但允许用户没订单(即右表为NULL),同时又想把“用户ID为NULL”的用户也纳入——错误写法:
SELECT * FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.id IS NULL
这会把所有 u.id 非NULL的用户全干掉,只剩NULL用户(且无订单记录)。正确做法是把NULL逻辑放进 ON 条件,或用 OR 扩展JOIN条件,再在 WHERE 中只筛真正需要的业务条件。
实际建模中更推荐提前处理NULL
靠SQL运行时硬补NULL匹配,既难读又影响性能(尤其大表JOIN时,OR 条件常导致索引失效)。更可持续的做法是:
- 在ETL或视图层统一用
COALESCE(user_id, 'UNK')或负数占位符填充NULL,确保JOIN字段稳定可索引 - 对业务语义明确的“无归属”场景,建独立维度值(如
user_id = -999表示“未登录用户”),比NULL更可控 - 检查上游数据质量,确认NULL是合理缺失,还是因ETL失败、类型转换错误导致的意外空值
NULL参与JOIN不是语法bug,而是SQL逻辑的必然结果;但把它当成“默认行为”去适配,往往意味着模型或数据准备环节已经埋了隐患。

















