<p>最稳的单列空值统计写法是 COUNT(*) - COUNT(列名),因其简洁、高效、兼容性强,且不依赖数据库版本或隐式类型转换。</p>

怎么用 COUNT 统计单列空值数量
直接查某列有多少空值,最稳的写法是 COUNT(*) - COUNT(列名)。因为 COUNT(列名) 会自动跳过 NULL,而 COUNT(*) 算所有行,差值就是该列的空值数。
别用 COUNT(NULLIF(列名, 列名)) 或 SUM(CASE WHEN 列名 IS NULL THEN 1 ELSE 0 END) —— 写法长、可读性差,且在某些旧版 MySQL 或 PostgreSQL 中可能触发隐式类型转换问题。
示例:查 users 表中 email 列的空值数:
SELECT COUNT(*) - COUNT(email) AS email_null_count FROM users;
-
COUNT(email)对NULL完全静默,不报错也不计入 - 如果
email是TEXT或带索引的字段,这个写法依然高效,优化器通常能走行数统计(尤其 MyISAM 或 PostgreSQL 的表级统计) - 注意:如果整张表为空,结果是
0,不是NULL—— 这点常被误判为“没查到”
想看多列空值组合分布?必须用 GROUP BY + IS NULL
当你要知道 “email 为空但 phone 不为空” 有多少条,“两者都为空” 又有多少条,就得把 IS NULL 当成布尔值参与分组。数据库里没有原生布尔类型分组,得靠 CASE 或直接用表达式(部分数据库支持)。
PostgreSQL 和 SQL Server 允许直接写 GROUP BY email IS NULL, phone IS NULL;MySQL 8.0+ 也支持,但 5.7 不行,得转成数值。
示例(兼容 MySQL 5.7+ 和 PostgreSQL):
SELECT<br> CASE WHEN email IS NULL THEN 1 ELSE 0 END AS email_is_null,<br> CASE WHEN phone IS NULL THEN 1 ELSE 0 END AS phone_is_null,<br> COUNT(*) AS cnt<br>FROM users<br>GROUP BY email_is_null, phone_is_null;
- 输出四行: (0,0), (0,1), (1,0), (1,1),对应“都不空、仅 phone 空、仅 email 空、都空”
- 别在
GROUP BY里直接写email IS NULL试 MySQL 5.7 —— 会报ERROR 1055,提示非聚合列不在 GROUP BY 中 - 如果列有上百万行,这个查询会扫全表,没索引加速;但加索引对
IS NULL效果有限,除非用函数索引(如 PostgreSQL 的CREATE INDEX ON users ((email IS NULL)))
COUNT(*) 和 GROUP BY 混用时的常见翻车点
有人想“一行查出每列空值数 + 分组分布”,于是写 SELECT COUNT(*), COUNT(col1), ... GROUP BY col2 IS NULL —— 这几乎一定报错或结果错乱。
根本原因是:混合了聚合和非聚合字段,且 GROUP BY 的粒度与 COUNT(*) 的语义冲突。数据库不知道你到底想按什么层级汇总。
- 要么全聚合(不写
GROUP BY,只统计总数) - 要么明确分组维度(如上面的
email_is_null, phone_is_null),所有SELECT里的非聚合字段必须出现在GROUP BY中 - MySQL 5.7 默认开启
sql_mode=STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY,这类语句直接拒绝执行;关掉它只是掩盖问题,不是解决 - 如果真要拼在一个结果集里,用子查询或 CTE,别硬塞进一个
SELECT
空值统计结果为什么和 SELECT * 数得不一样
最常被忽略的一点:空值判断依赖字段定义本身,而不是“看起来空”。比如 ''(空字符串)、' '(空格)、'NULL'(字符串)都不是 NULL,IS NULL 查不出来。
另外,某些 ORM 或导出工具会在空值位置填默认值(如 0、'N/A'),导致你肉眼看到“空”,但数据库里根本不是 NULL。
- 先确认真实空值:用
SELECT * FROM table WHERE col IS NULL LIMIT 5看几条,别信 Excel 或管理界面的显示 - 字符串类字段,常需同时检查:
col IS NULL OR TRIM(col) = '' - 数字类字段,
0和NULL语义完全不同,统计时千万别混用COALESCE(col, 0)后再算 —— 那是在改数据,不是在统计
空值分布这事,起点永远得是“你真正想定义什么是空”,而不是数据库返回了什么。










