IS NULL 是什么
NULL 表示未知或缺失,不能用等号判断:a = NULL 的结果是 UNKNOWN,而 WHERE 只保留求值为 TRUE 的行,所以这类写法永远查不出数据却也不报错。正确写法是 IS NULL 与 IS NOT NULL。NULL 还会影响统计:COUNT(col) 会跳过 NULL,AVG 的分母是非 NULL 行数,NULL 参与算术或字符串拼接通常让结果也变成 NULL。理解这三条规则,才能写对判空、补数据和缺失率统计。
基本信息
| 判空写法 | IS NULL 与 IS NOT NULL,都是标准写法 |
|---|---|
| 错误写法 | = NULL、<> NULL,结果恒为 UNKNOWN,永远不成立 |
| 聚合影响 | COUNT(col)、SUM、AVG、MIN、MAX 都忽略 NULL |
| 运算影响 | NULL 参与算术得到 NULL;MySQL 的 CONCAT 遇 NULL 得 NULL |
| 唯一索引 | 多个 NULL 通常互不冲突,唯一约束对 NULL 不生效 |
| 平台差异 | PostgreSQL 另有 IS DISTINCT FROM 做 NULL 安全比较 |
典型使用场景
- 找缺失数据:手机号、邮箱为空的行,用来评估字段填充率
- 补数据前置检查:更新前先确认到底有多少行真的需要补
- 统计非空量:COUNT(col) 与 COUNT(*) 相减得到缺失行数
- 区分未填与空串:业务上要区分 NULL 与 '' 时先统一口径
- 排除缺失再运算:WHERE col IS NOT NULL 后再做函数计算,避免整列变 NULL
- 分组观察:NULL 会被归到同一组,用来发现某维度有大量缺失
示例
示例
SELECT * FROM users WHERE phone IS NULL; # 手机号缺失的行
SELECT * FROM users WHERE phone IS NOT NULL; # 手机号已填的行
SELECT COUNT(*) AS all_rows,
COUNT(phone) AS has_phone,
SUM(phone IS NULL) AS null_phone, # MySQL:布尔值即 0/1
COUNT(*) - COUNT(phone) AS missing_phone
FROM users;
-- PostgreSQL:布尔值不能直接求和,先转换
SELECT SUM((phone IS NULL)::int) FROM users;NULL 在各处的表现
| 位置 | 表现 | 注意 |
|---|---|---|
| 比较 a = NULL | 结果为 UNKNOWN | 永远不成立,必须用 IS NULL |
| 算术 NULL + 1 | 结果为 NULL | 金额计算前先兜底 |
| 字符串拼接 | MySQL 得 NULL,PG 的 concat 忽略 | 统一用 COALESCE 兜底 |
| 聚合 COUNT(col) | 跳过 NULL | 与 COUNT(*) 结果不同 |
| GROUP BY | 多个 NULL 归为同一组 | WHERE col = NULL 取不出这一组 |
| 唯一索引 | 多个 NULL 互不冲突 | 需要强唯一时要额外约束 |
PostgreSQL 提供 IS DISTINCT FROM:a IS DISTINCT FROM b 在其中一个为 NULL 时返回 true,用于 NULL 安全的差异比对,比 <> 更可靠。
排序时 NULL 的位置由数据库决定:MySQL 把 NULL 视为最小排在最前,PostgreSQL 默认排在最后;需要固定位置时 PostgreSQL 用 NULLS FIRST / LAST,MySQL 用 ISNULL(col) 表达式参与排序。
WHERE col <> 'x' 会漏掉 col 为 NULL 的行。业务上如果要把缺失行也算作不符合条件,必须显式写上 OR col IS NULL。
NULL 与空串不是一回事:空串长度为零、参与比较与拼接都有确定行为,建模时要提前决定是否禁止空串,避免同一语义出现两种存法。
判空实践要点
- 建表时明确每列的可空性:关键字段加 NOT NULL 约束,能挡掉一批脏数据
- 用默认值代替 NULL 是常见取舍:数字用 0、字符串用空串,代价是失去未填与已填的区分
- 缺失率统计先定口径:分母是所有行还是符合条件的行为,写进报表说明
- 判空与索引:NULL 占比低时 IS NULL 可能走索引,占比高时优化器倾向全表扫描
- 接口层统一 NULL 的表示:JSON 里是 null 还是缺字段,前后端要提前对齐
- 补数据脚本先备份原值:UPDATE 补 NULL 字段前确认影响行数,并保留回滚依据
判断一个列是否真的缺数据,不要只看 IS NULL:业务上未填也可能写成空串或 0,统计前先确认历史数据的实际写法。
排查查询结果为空时先怀疑 NULL:条件里出现 <>、NOT IN、NOT LIKE 这些写法时,都要确认相关列是否可能为 NULL,这是最常见的误判来源。
统计缺失率时用 COUNT(*) 减 COUNT(col) 最直观,也避开了布尔表达式在不同数据库写法上的差异,跨库 SQL 更省事。
在接口契约里把 NULL 的传输形式约定清楚能省很多事:是返回 null、返回空串还是省略字段,前后端提前对齐,比事后逐个字段排查高效得多。