NULL 的语义是什么
NULL 表示未知。掌握它的几条规则就能避免大量隐蔽问题:参与比较得到 UNKNOWN(既不是 TRUE 也不是 FALSE),参与算术得到 NULL,参与字符串拼接在 MySQL 里得到 NULL、在 PostgreSQL 的 concat 函数里被忽略、用 || 运算符时得到 NULL。聚合函数除 COUNT(*) 外都忽略 NULL 输入。唯一索引中的多个 NULL 通常视为互不冲突。GROUP BY 会把多个 NULL 归为同一组,但 WHERE col = NULL 取不出这一组。
基本信息
| 三值逻辑 | TRUE、FALSE、UNKNOWN;WHERE 只保留 TRUE |
|---|---|
| 比较 | NULL 参与任何比较都得 UNKNOWN |
| 算术 | NULL 参与运算结果为 NULL |
| 拼接 | MySQL 的 CONCAT 得 NULL;PG 的 concat 忽略,|| 得 NULL |
| 聚合 | 除 COUNT(*) 外都忽略 NULL 输入 |
| 唯一索引 | 多个 NULL 通常不互斥 |
| 分组 | 多个 NULL 归为同一组 |
典型使用场景
- 排查查询结果为空:检查条件里是否混入 NULL 比较
- 统计口径核对:COUNT(*) 与 COUNT(col) 的差异来源
- 默认值设计:决定用 NULL 还是用默认值表示未填
- 唯一约束设计:确认是否允许重复的 NULL
- 分组观察数据质量:NULL 归组时暴露缺失分布
- 跨库迁移核对:字符串拼接与聚合的 NULL 行为差异
行为演示
行为演示
SELECT NULL = NULL; # UNKNOWN,不是 TRUE
SELECT NULL <> NULL; # UNKNOWN
SELECT NULL + 1; # NULL
SELECT CONCAT('a', NULL); # MySQL:NULL
SELECT 'a' || NULL; # PostgreSQL:NULL
SELECT concat('a', NULL); # PostgreSQL:忽略 NULL,得到 a
SELECT NULL IS NULL; # TRUENULL 在各处的表现
| 位置 | 表现 | 实践建议 |
|---|---|---|
| 比较运算 | 得到 UNKNOWN | 判空用 IS NULL |
| 算术运算 | 结果为 NULL | 先用 COALESCE 兜底 |
| 字符串拼接 | MySQL 得 NULL,PG 的 concat 忽略 | 统一用 COALESCE 兜底 |
| 聚合函数 | 忽略 NULL 输入 | 口径上明确 NULL 是否计入 |
| GROUP BY | 多个 NULL 归为一组 | 需要单独看缺失分布时很有用 |
| 唯一索引 | 多个 NULL 并存 | 需要强唯一时额外加约束 |
PostgreSQL 的 concat() 函数忽略 NULL 参数,而 || 运算符遇到 NULL 返回 NULL,函数与运算符行为不一致,迁移时逐条核对。
统计平均值时 AVG(col) 的分母是非 NULL 行数,与 SUM(col) 除以 COUNT(*) 的结果不同,按业务口径选择并写明。
WHERE 里的 <>、NOT IN、NOT LIKE 都会把 NULL 行排除在外,需要把缺失行也算进来时必须显式补 OR col IS NULL。
使用要点
- 建模时先定每个列的可空性,关键字段加 NOT NULL
- 决定用 NULL 还是默认值表示未填,全库统一
- 判空一律用 IS NULL 与 IS NOT NULL
- 聚合口径写清楚:是否把 NULL 行算进总数
- 跨库迁移时核对拼接与聚合的 NULL 行为差异
- 在数据质量报表里单独跟踪 NULL 比例的变化趋势