null不是值而是缺失标记,=null永远不匹配,必须用is null;not in遇null失效,应改用not exists或过滤null;标量子查询返回null会导致计算结果为null,需用coalesce处理;聚合前须在子查询内过滤异常值。

子查询里写 IS NULL 才有效,= NULL 永远不匹配
SQL 中 NULL 不是值,而是“缺失”的标记,所以任何用 =、!= 或 和 NULL 比较的结果都是 UNKNOWN,而 WHERE 只保留 TRUE 行。这意味着:
– WHERE col = NULL 查不到任何数据
– WHERE col != NULL 同样查不到任何数据
必须改用 IS NULL 或 IS NOT NULL —— 这不是风格问题,是语义强制要求。
在子查询内部(比如 SELECT * FROM orders WHERE status IS NULL)也一样适用,错写成 = NULL 就等于没过滤。
NOT IN 遇到子查询返回 NULL 就整个失效
这是最常踩的坑:
– WHERE id NOT IN (SELECT user_id FROM logs),只要 logs.user_id 里有任意一个 NULL,整条 NOT IN 条件就变成 UNKNOWN,结果集为空
– 原因是三值逻辑:id NOT IN (1, 2, NULL) 等价于 id != 1 AND id != 2 AND id != NULL,最后一项永远是 UNKNOWN
替代方案只有两个:
– 显式排除 NULL:SELECT user_id FROM logs WHERE user_id IS NOT NULL
– 或直接换用 NOT EXISTS:WHERE NOT EXISTS (SELECT 1 FROM logs l WHERE l.user_id = users.id),它天然绕过 NULL 陷阱
标量子查询返回 NULL 时,主表字段显示为空但不报错
比如写 SELECT name, (SELECT email FROM contacts WHERE user_id = users.id) AS contact_email FROM users:
– 如果某用户没有关联联系人,子查询返回 NULL,contact_email 字段就为空,但你可能误以为“数据丢了”或“关联失败”
– 更危险的是参与计算:(SELECT price FROM items WHERE id = o.item_id) * tax_rate,只要子查询返回 NULL,整列结果就是 NULL
正确做法是紧贴子查询外层加 COALESCE:COALESCE((SELECT email FROM contacts WHERE user_id = users.id), 'N/A'),而不是在子查询内部写 COALESCE(email, 'N/A')——后者仍可能返回空集,结果还是 NULL
聚合计算前必须在子查询内过滤异常值,不能靠外层 WHERE
比如想算平均价格:
– 错误:SELECT AVG(price) FROM (SELECT * FROM orders) t WHERE price > 0,这个 WHERE 对 AVG() 无影响,因为它是对外层派生表过滤,而 AVG() 已经在子查询结果上计算了
– 正确:把条件塞进子查询里:SELECT AVG(price) FROM (SELECT price FROM orders WHERE price IS NOT NULL AND price BETWEEN 0 AND 10000) t
尤其注意 NULL、负数、超大离群值——它们不会报错,但会悄悄污染结果。子查询不是命名别名工具,它是逻辑隔离层,清洗动作必须落在里面。
真正容易被忽略的,是 NULL 在子查询不同位置引发的语义断裂:它可能让条件失效、让计算归零、让关联静默丢失,而且全程不报错。处理它不能靠“试试看”,得从每一层子查询的 WHERE、SELECT、JOIN 条件里主动预判和拦截。











