首页 > 数据库 > MySQL > 第72期 NULL 字段索引会不会失效?
索引本身可以正常存储 NULL,不会因为字段有 NULL 就整个索引失效;
失效只出现在 特定查询写法,不是字段存 NULL 导致索引废掉;
B+Tree 索引中
NULL 统一放在索引最左侧,MySQL 完全支持索引扫描 NULL 值。
CREATE TABLE t(
id INT PRIMARY KEY,
phone VARCHAR(20),
KEY idx_phone(phone));
-- 插入大量数据,包含很多phone=null
insert into t value('1','1380000001');
insert into t value('2','1380000002');
insert into t value('3','1380000003');
insert into t value('4','1380000004');
insert into t value('5','1380000005');
insert into t value('6','1380000006');
insert into t value('7','1380000007');
insert into t value('8','1380000008');
insert into t value('9',NULL);
insert into t value('10',NULL);
insert into t value('11',NULL);
insert into t value('12',NULL);
EXPLAIN SELECT * FROM t WHERE phone IS NULL;

type=ref/range,使用
idx_phone,
不会全表扫描。
EXPLAIN SELECT * FROM t WHERE phone IS NOT NULL;

误区纠正:很多人以为 NULL 不走索引,这是老版本谣言,MySQL5.6 之后完全支持 NULL 索引检索。
=、
!= 匹配 NULL → 索引失效(重点)SELECT * FROM t WHERE phone = NULL;
SQL 逻辑:
SELECT * FROM t WHERE phone != NULL;
IS NOT NULL。KEY idx_a_b (a,b)WHERE a=1; -- 走索引 WHERE a=1 AND b IS NULL; -- 走索引 WHERE a IS NULL AND b=2; -- 走索引 WHERE b=2; -- 丢失最前列,不走索引(和NULL无关)
SELECT * FROM t WHERE id NOT IN (1,2,NULL);
NOT IN 内存在 NULL,逻辑运算结果永未知,数据库直接返回空结果,不会走索引,属于逻辑陷阱,不是索引不支持 NULL。SELECT * FROM t WHERE phone IS NULL;
FORCE INDEX(idx_phone) 强制走索引。SELECT phone,COUNT(*) FROM t GROUP BY phone;ORDER BY phone;
索引有序存储 NULL,GROUP BY、ORDER BY 均可利用索引避免临时表 / 文件排序。
查询 NULL 必须用
IS NULL / IS NOT NULL,禁止
= NULL / != NULL;
若业务 90% 数据都是 NULL,该字段单独建索引收益极低,可考虑:
unknown),提升索引选择性;ANALYZE TABLE t; 更新统计后重新执行。字段存 NULL 不会让索引永失效;
IS NULL / IS NOT NULL 正常走索引;
= NULL / != NULL / NOT IN(...,NULL) 会不走索引;
NULL 过多导致过滤性差,优化器可能主动放弃索引扫描。
单列 B 树索引:不会存储全 NULL 行一条记录索引字段全部为 NULL → 这条记录不会写入 B 树索引。
联合复合 B 树索引:只要至少一列非 NULL,整条索引条目会保存;所有列同时 NULL 才不存。
查询写法不同,会出现「看似索引失效」,分场景逐一说明。
t(id,email),单列索引
idx_email(email)explain plan for select * from t where email is null; -- PLAN_TABLE:TABLE ACCESS FULL
建立 反向索引 / 位图索引(位图索引会存 NULL)
建立函数索引,把 NULL 转为固定值:
create index idx_email_nn on t(nvl(email,'#NULL#'));-- 查询时对齐写法 select * from t where nvl(email,'#NULL#') = '#NULL#';
索引
idx_a_b (a, b)
规则: 只要索引中任意一列不为 NULL,该行就会写入索引;只有 a AND b 同时为 NULL 才不存。
create table t(a number,b varchar2(20)); create index idx_ab on t(a,b); -- 能走索引 select * from t where a is null and b='test'; -- 全表扫描 select * from t where a is null and b is null;
单列唯一索引:null 不存入索引,
col is null 全表扫;
复合唯一索引:字段不同时 NULL 就存入索引;
允许多条 NULL(唯一约束仅限制非空值唯一性)。
位图索引
会存储 NULL,
where col is null 直接走位图索引,无失效问题。
适用场景:字段基数低(性别、状态、少量枚举),不适合 DML 频繁更新表。
缺点:并发 DML 会锁整张表,OLTP 业务慎用。
where col = null
Oracle 中
null = 任意值
结果 UNKNOWN,无返回数据,必然全表扫描,必须用
is null
。
select * from t where id not in (1,2,null);
3.NULL 数据量占比极高哪怕索引能检索,若满足条件数据超表 30%,优化器自动选择全表扫描,不是索引失效,是代价判断。
| 特性 | MySQL B + 树 | Oracle B 树 |
|---|---|---|
| 单列 NULL 是否存入索引 | 存入,IS NULL 走索引 | 不存入,IS NULL 全表扫 |
| 复合索引全 NULL 行 | 存入 | 不存入 |
| IS NULL 查询 | 天然支持索引 | 单列失效,复合部分场景可用 |
| 位图索引 | 无 | 支持 NULL 检索 |
单列字段经常查
is null:
联合索引尽量保证查询条件至少一列非空,避免双 NULL 条件;
= null,统一标准
is null / is not null。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/12798004/viewspace-3176183/ ,如需转载,请注明出处,否则将追究法律责任。