SQL中NULL是缺失值标记,=NULL永远不生效,须用IS NULL;IN子查询遇NULL整体失效;计算中任一NULL致结果为NULL;关联时NULL引发隐式丢失;应预判并显式处理NULL。

WHERE 子句中用 = NULL 判断永远不生效
SQL 里 NULL 不是值,而是“缺失值”的标记,所以任何和 NULL 的等值比较(=、!=、)结果都是 UNKNOWN,而 WHERE 只接受 TRUE 的行。这就导致 WHERE col = NULL 看似想筛出空值,实际一条都捞不到。
- 必须用
IS NULL或IS NOT NULL—— 这是唯一标准写法 - 别被某些方言(比如 MySQL 的
<=>)带偏,它虽能判NULL相等,但只在特定场景(如去重)有用,子查询里反而增加理解成本 - 在子查询的
WHERE或HAVING中混用= NULL,常导致“数据明明有却查不到”的静默失败
IN 子查询遇到 NULL 会整个失效
当子查询返回结果包含 NULL,比如 SELECT id FROM orders WHERE status IN (SELECT code FROM status_ref),而 status_ref.code 里有 NULL,整个 IN 表达式就变成 UNKNOWN,这一行直接被过滤掉——不是漏掉 NULL,而是所有匹配都失效。
- 解决方法:显式排除
NULL,写成SELECT code FROM status_ref WHERE code IS NOT NULL - 如果业务上确实需要把
NULL当作一个合法状态,改用EXISTS+ 关联更安全,例如EXISTS (SELECT 1 FROM status_ref s WHERE s.code = o.status),它天然忽略NULL比较问题 - 注意:MySQL 8.0+ 对
IN的优化可能掩盖这个问题,但 PostgreSQL 和 SQL Server 会严格按标准执行,跨库迁移时容易翻车
SELECT 子句中的 NULL 参与计算导致整列变 NULL
子查询作为派生表或字段表达式时,只要任意参与运算的字段是 NULL,整个算术/字符串表达式结果就是 NULL。比如 (SELECT price * tax_rate FROM taxes),哪怕 price 有值,tax_rate 是 NULL,结果就是 NULL,且不会报错。
- 用
COALESCE显式兜底最稳妥:COALESCE(tax_rate, 0)或COALESCE(tax_rate, 0.08) - 避免在子查询里嵌套多层计算再
COALESCE,尽量把COALESCE放在最靠近数据源的地方,否则调试时很难定位哪一环掉了NULL -
NULLIF在某些场景比COALESCE更合适,比如防止除零:amount / NULLIF(denom, 0)
关联子查询里 NULL 导致重复或丢失行
当子查询用于 JOIN 条件或相关标量子查询(如 SELECT (SELECT name FROM users u WHERE u.id = o.user_id)),若关联字段为 NULL,结果要么是空值(标量),要么无法匹配(JOIN),但不会报错,容易误以为数据丢了或重复了。
- 标量子查询必须加
COALESCE包裹,否则主表某行对应子查询返回NULL,该字段显示为空,但你可能以为是没查到数据 - 用
LEFT JOIN替代相关子查询时,记得在ON条件里也处理NULL,比如ON u.id = o.user_id AND u.id IS NOT NULL,否则NULL = NULL在某些数据库(如 SQLite)会被当成真 - PostgreSQL 的
LATERAL子查询对NULL更敏感,一旦外层值为NULL,整个子查询不执行,结果为NULL,这点和普通相关子查询行为一致,但调试时更难察觉
真正麻烦的不是 NULL 本身,而是它不报错、不警告、不提示,只悄悄让逻辑跑偏。每次写子查询前,先花十秒想清楚:哪些字段可能为 NULL?它们出现在比较、计算、还是关联位置?这比事后查三天数据不一致要省力得多。

















