MySQL中NULL导致查询逻辑失效、统计偏差和索引失效三类静默异常,因其代表“未知”而非值,使布尔判断返回UNKNOWN,WHERE仅保留TRUE行,COUNT跳过NULL,IS NULL难走索引,建表时应明确NOT NULL与默认值。

MySQL中给字段赋NULL值本身不会直接导致报错,但会让后续的查询行为变得“不可预测”——不是语法错误,而是逻辑失效、结果丢失、统计偏差这三类静默异常。根本原因在于NULL不是值,是“未知”状态,它让SQL的布尔逻辑、比较运算、聚合函数全部偏离直觉。
WHERE条件里用!=或NOT IN时,NULL行被悄悄过滤
这是最常踩的坑:业务想查“非已完成”的记录,写WHERE status != 1,结果status为NULL的行全不见了。因为NULL != 1的结果是UNKNOWN,而WHERE只保留TRUE行。
-
WHERE col != 'a':排除'a',也排除NULL(业务上常期望NULL算作“非a”) -
WHERE col NOT IN ('a', 'b'):只要子查询或列表里有一个NULL,整个表达式返回UNKNOWN,结果集为空 - 正确写法应显式补全:
WHERE col != 'a' OR col IS NULL或改用NOT EXISTS
COUNT和DISTINCT统计不准,数据凭空“消失”
COUNT(col)跳过NULL是设计如此,但容易误用。比如统计用户数用COUNT(phone),结果比COUNT(*)少一大截——不是漏数据,是phone为空的用户被无视了。
-
COUNT(*)统计所有行;COUNT(col)只统计col非NULL的行 -
COUNT(DISTINCT col1, col2):任一列为NULL,整行不参与去重 - 聚合函数如
SUM()、AVG()遇到NULL会跳过,结果可能为NULL,引发应用层空指针
IS NULL查询难走索引,慢得离谱
InnoDB的B+树索引默认不存储NULL值,所以WHERE col IS NULL大概率触发全表扫描——EXPLAIN里看到type: ALL、key: NULL就是这个原因。
- 单列索引对
IS NULL支持弱,尤其当NULL占比高时,优化器更倾向放弃索引 - 联合索引中,若最左列是
NULL(如INDEX(a,b),WHERE a IS NULL AND b = 1),索引完全失效 - 唯一索引允许多个
NULL,反而让IS NULL无法利用唯一性加速
ALTER TABLE加NOT NULL约束直接报错1138
想收紧字段约束时,ALTER TABLE t MODIFY col VARCHAR(20) NOT NULL会立刻失败,报ERROR 1138 (22003): Invalid use of NULL value。
- MySQL在变更前强制校验:只要现有数据中存在任意一行
col IS NULL,就拒绝执行 - 这不是bug,是保护机制——防止约束生效后出现非法数据
- 必须先清洗:
UPDATE t SET col = 'default' WHERE col IS NULL,再跑MODIFY
真正麻烦的从来不是NULL本身,而是它在不同上下文里表现不一致:有时被忽略,有时让整个逻辑坍塌,有时连索引都绕不开。别指望靠“加个索引”或“写个IFNULL”临时打补丁,从建表开始就明确字段是否允许NULL、配什么默认值,才是减少这类异常的根因。


















