left join后where过滤右表字段会导致数据减少,因为where在连接后执行,将右表为null的行(如b.status为null)全部过滤掉,使left join退化为inner join;正确做法是将右表条件移至on子句,如on a.id = b.a_id and b.status = 'active'。

LEFT JOIN后WHERE里写右表条件,为什么数据突然少了?
因为WHERE对右表字段加条件(比如WHERE b.status = 'active')会把所有右表没匹配上的行(即b.status为NULL的行)全过滤掉——NULL = 'active'返回UNKNOWN,而WHERE只保留TRUE行。
这不是bug,是SQL三值逻辑的必然结果。你写的明明是LEFT JOIN,实际效果却等价于INNER JOIN。
- 正确做法:把右表过滤条件挪到
ON子句,如LEFT JOIN b ON a.id = b.a_id AND b.status = 'active' - 退而求其次:在
WHERE里显式兼容NULL,写成WHERE b.status = 'active' OR b.status IS NULL - 绝对别写
WHERE ISNULL(b.status, 'active') = 'active'——它让索引失效,且语义混乱
JOIN条件里a.code = b.code为何不连上两个NULL?
因为NULL = NULL返回UNKNOWN,不是TRUE,所以这行被排除。这是SQL标准行为,不是数据库实现差异。
常见于外键缺失、可选字段未填充、ETL清洗不彻底等场景。
- PostgreSQL和MySQL 8.0.16+支持
IS NOT DISTINCT FROM,可直接写ON a.code IS NOT DISTINCT FROM b.code - SQL Server或Oracle需手写等价逻辑:
ON (a.code = b.code) OR (a.code IS NULL AND b.code IS NULL) - 避免用
COALESCE(a.code, 0) = COALESCE(b.code, 0)——函数包装破坏索引,强制全表扫描
NOT IN子查询一含NULL,整条查询就失效?
是的。col NOT IN (1, 2, NULL)等价于col != 1 AND col != 2 AND col != NULL,而最后一项col != NULL永远是UNKNOWN,整个表达式变为UNKNOWN,该行被过滤。
这不是“漏掉NULL”,而是“所有行都判不定”,结果集可能为空,即使业务上存在合法匹配。
- 首选方案:改用
NOT EXISTS,如WHERE NOT EXISTS (SELECT 1 FROM ref WHERE ref.code = t.code) - 次选方案:子查询里显式排除NULL,
SELECT code FROM ref WHERE code IS NOT NULL - 别依赖
IN/NOT IN自动处理NULL,跨库行为不一致(MySQL 8.0+可能优化掩盖问题,PG/SQL Server严格执行)
标量子查询或计算字段遇到NULL,为什么结果全变NULL?
SQL规定:任何运算中只要有一个操作数是NULL,结果就是NULL。比如price * tax_rate,哪怕price有值,tax_rate是NULL,结果就是NULL;CONCAT(first_name, ' ', last_name)中任一字段为NULL,整串变NULL。
- 标量子查询必须包裹
COALESCE,如(SELECT COALESCE(name, 'unknown') FROM users u WHERE u.id = o.user_id) - 视图定义中优先在原始字段层兜底:
COALESCE(tax_rate, 0.08) AS effective_tax_rate,而不是在外层再包 - 字符串拼接要分别处理:
CONCAT(COALESCE(first_name, ''), ' ', COALESCE(last_name, '')) - 防除零用
NULLIF:amount / NULLIF(denom, 0),比COALESCE(denom, 1)更符合业务语义
最麻烦的不是写法,而是NULL在关联、子查询、计算中都不报错,只是静默消失——查不到数据时,先盯住有没有NULL参与了比较或运算。











