SQL 中 NULL 和三值逻辑如何影响查询结果?
简化版
NULL 表示未知或缺失,不能用 = NULL 判断,应该用 IS NULL;SQL 条件逻辑有 TRUE/FALSE/UNKNOWN 三种结果,WHERE 只保留 TRUE。
详细版
在 SQL 里,NULL 不是空字符串、不是 0,也不是一个普通值。表达式 col = NULL 的结果不是 TRUE 或 FALSE,而是 UNKNOWN,因此不会被 WHERE 保留下来。
正确判断空值要使用:
WHERE col IS NULL
WHERE col IS NOT NULL
三值逻辑会影响比较、IN/NOT IN、聚合函数、排序和唯一约束等场景。比如 COUNT(col) 不统计 NULL,COUNT(*) 统计行数;NOT IN 子查询中如果含 NULL,结果可能出乎意料。
完整版教学
一、NULL 不是普通值
NULL 更接近“未知、缺失、不可用”。既然未知,就不能说它等于某个值,也不能说它不等于某个值。
SELECT *
FROM users
WHERE phone = NULL;
这条 SQL 通常查不到手机号为空的用户,因为 phone = NULL 的判断结果是 UNKNOWN。
正确写法是:
SELECT *
FROM users
WHERE phone IS NULL;
记忆钩子:遇到
NULL,不要问“等不等于”,要问“是不是空值状态”。
二、三值逻辑是什么
普通编程语言里条件常是两值逻辑:true 或 false。SQL 引入 NULL 后,比较结果多了一个 UNKNOWN。
5 = 5 -> TRUE
5 = 6 -> FALSE
NULL = 5 -> UNKNOWN
NULL = NULL -> UNKNOWN
WHERE 子句只保留结果为 TRUE 的行,FALSE 和 UNKNOWN 都会被过滤掉。
| 表达式 | 结果 | 是否被 WHERE 保留 |
|---|---|---|
age > 18 且 age=20 | TRUE | 是 |
age > 18 且 age=16 | FALSE | 否 |
age > 18 且 age=NULL | UNKNOWN | 否 |
这就是很多空值数据“莫名消失”的根源。
三、AND/OR 遇到 UNKNOWN 怎么算
三值逻辑不是简单把 UNKNOWN 当 FALSE。它表示不知道,因此和其他条件组合时要按语义推导。
TRUE AND UNKNOWN -> UNKNOWN
FALSE AND UNKNOWN -> FALSE
TRUE OR UNKNOWN -> TRUE
FALSE OR UNKNOWN -> UNKNOWN
例如:
SELECT *
FROM users
WHERE age > 18 AND phone = '13800000000';
如果 age=20 但 phone=NULL,第二个条件是 UNKNOWN,整体是 UNKNOWN,不会返回。
如果条件是 age > 18 OR phone = '13800000000',age=20 已经能让整体为 TRUE,这行会返回。
四、NOT IN 和 NULL 是高频陷阱
NOT IN 的坑非常经典。假设黑名单表里有一个 NULL:
-- blacklist.user_id: 2, NULL
SELECT id
FROM users
WHERE id NOT IN (SELECT user_id FROM blacklist);
对 id=1 来说,判断近似变成:
1 <> 2 AND 1 <> NULL
TRUE AND UNKNOWN -> UNKNOWN
结果不是 TRUE,所以 id=1 也不会返回。很多人以为应该返回“不在黑名单”的用户,结果查出 0 行。
更稳的写法是过滤子查询空值,或使用 NOT EXISTS:
SELECT u.id
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM blacklist b
WHERE b.user_id = u.id
);
五、聚合函数对 NULL 的处理
聚合函数也有明确规则:COUNT(*) 统计行数,COUNT(col) 统计非 NULL 的列值数量。
-- scores: 100, 80, NULL
SELECT
COUNT(*) AS row_count,
COUNT(score) AS score_count,
AVG(score) AS avg_score
FROM exams;
结果是:
row_count | score_count | avg_score
3 | 2 | 90
AVG(score) 只用 100 和 80 计算,不把 NULL 当 0。如果业务需要把缺考按 0 分算,必须显式写 COALESCE(score, 0)。
六、排序、唯一约束和默认值也要小心
不同数据库对 NULL 排序位置可能有差异,有的默认在前,有的默认在后,也可能支持 NULLS FIRST/LAST。
SELECT id, score
FROM exams
ORDER BY score DESC NULLS LAST;
唯一约束中 NULL 的行为也依赖数据库实现。很多数据库允许唯一列中出现多个 NULL,因为多个未知值不一定相等。
工程设计上,能不用 NULL 表示业务状态时要谨慎。比如订单状态不要用 NULL 表示“待支付”,更适合定义明确枚举值 pending。NULL 更适合确实未知或不存在的数据,如“用户尚未填写生日”。
七、常见误区与追问
- 误区:
NULL = NULL为真。 两个未知值不能被判断为相等,结果通常是UNKNOWN。 - 误区:
WHERE col != 1会返回NULL行。NULL != 1也是UNKNOWN,不会被WHERE保留。 - 误区:
COUNT(col)和COUNT(*)一样。 前者只统计非空列值,后者统计行数。 - 误区:
NOT IN子查询里有NULL没影响。 它可能让结果整体变成UNKNOWN,导致查不到预期数据。 - 追问:如何把 NULL 当默认值处理? 使用
COALESCE(col, default_value)。 - 追问:如何判断空字符串? 空字符串和
NULL不是一回事,通常用col = ''判断空字符串,用IS NULL判断空值。
八、加强记忆
NULL 题要抓住“未知”二字:比较会产生 UNKNOWN,WHERE 只要 TRUE,COUNT(col) 跳过空值,NOT IN 遇空值容易翻车。写 SQL 时看到空值相关条件,第一反应应是 IS NULL、IS NOT NULL、COALESCE、NOT EXISTS。