NULL 与索引的关系
NULL 与索引的关系常被误解。B 树索引会为 NULL 建立条目,所以 IS NULL 通常可以走索引;真正难走索引的是负向条件,比如不等于、NOT IN、NOT LIKE,它们在多数情况下会退化成扫描。能否命中以实际执行计划为准。
基本信息
| NULL 的存储 | InnoDB 与 PostgreSQL 的 B 树都会为 NULL 建立条目 |
|---|---|
| IS NULL | 一般可以走索引 |
| 负向条件 | 不等于、NOT IN、NOT LIKE 多数情况下无法走索引 |
| 唯一索引 | 两库默认允许多个 NULL 共存 |
| 验证方式 | 以 EXPLAIN 输出为准 |
典型使用场景
- 查询未被逻辑删除的记录
- 排查「明明有索引却走了全表扫描」
- 把负向条件改写成正向条件
- 筛选某字段为空的数据
- 做容量评估时把 NULL 条目算进去
示例
示例
-- 可走索引:B 树包含 NULL 条目
SELECT * FROM users WHERE deleted_at IS NULL;
-- 负向条件:多数情况退化为扫描
SELECT * FROM users WHERE status <> 'active';
SELECT * FROM users WHERE email NOT LIKE 'a%';
-- 改写思路:用状态枚举的正向条件替代负向条件
SELECT * FROM users WHERE status IN ('pending', 'locked');条件形态与索引
| 条件形态 | 通常能否走索引 | 说明 |
|---|---|---|
| col IS NULL | 通常可以 | B 树包含 NULL 条目 |
| col IS NOT NULL | 视选择性而定 | 命中大部分行时不划算 |
| col <> 值 | 多数不能 | 除非命中比例极低且统计支持 |
| NOT IN | 多数不能 | 还叠加 NULL 语义陷阱 |
| NOT LIKE '前缀%' | 不能 | 无法利用前缀有序性 |
NOT IN 有两个问题叠加:索引通常用不上,子查询里含 NULL 时结果还可能整段为空。改写成 NOT EXISTS 或正向枚举,往往一次解决两点。
NULL 会占索引空间:存在 NULL 的列,索引体积与选择性都会受影响,做容量评估时别忽略这部分。
统计信息里 NULL 的占比会影响优化器判断:NULL 很多时 IS NULL 的选择性变差,优化器可能放弃索引改走全表扫描,这和索引本身无关。
把「未知」和「空值」混在同一列里会让索引与查询都变含糊:状态与业务字段尽量用明确取值表达,NULL 只保留一个含义。