count(column_name) 统计每组非null值数量,天然忽略null;count()统计所有行;需统计null数量时用count()-count(column)或sum(case when column is null then 1 else 0 end)。

用 COUNT(column_name) 统计每组非 NULL 字段数量
直接对字段名用 COUNT() 就行,它天然忽略 NULL 值,只统计非 NULL 的行数。这点和 COUNT(*) 不同——后者统计所有行,不管字段是否为 NULL。
常见错误是写成 COUNT(COALESCE(column_name, 0)) 或 COUNT(IFNULL(...)),反而把 NULL 转成非 NULL 值再计数,结果偏高。
- GROUP BY 后跟分组字段,
COUNT(字段名)自动按组聚合 - 字段类型不影响统计逻辑:数值、字符串、日期都一样处理
- 如果该字段在某组中全部为 NULL,
COUNT(字段名)返回 0,不是 NULL
SELECT category, COUNT(price) AS non_null_price_count FROM products GROUP BY category;
需要同时统计 NULL 和非 NULL 数量?用条件聚合
当不只是要“非 NULL 个数”,还要知道“NULL 有多少个”或“总行数多少”,就得靠 SUM() + CASE 或 COUNT() 组合。
COUNT(*) 是全量行数,COUNT(col) 是非 NULL 行数,两者相减就是 NULL 行数——但这个差值在某组全为 NULL 时会是负数吗?不会,因为 COUNT(col) 在全 NULL 时为 0,COUNT(*) 是真实行数,相减结果准确。
-
SUM(CASE WHEN price IS NULL THEN 1 ELSE 0 END)显式统计 NULL 次数 -
COUNT(*) - COUNT(price)更简洁,但可读性略低 - 注意别写成
COUNT(price IS NULL)——多数数据库(如 MySQL)里这等价于COUNT(1),永远返回行数
SELECT category,
COUNT(*) AS total_rows,
COUNT(price) AS non_null_price,
COUNT(*) - COUNT(price) AS null_price
FROM products
GROUP BY category;
多字段联合统计非 NULL 数量?别嵌套,用多个 COUNT
想看每组里 price、stock、discount 各自的非 NULL 数量,最稳妥的方式是并列写多个 COUNT(字段)。不要试图用 COUNT(COALESCE(price, stock, discount)) 这类写法——它统计的是“至少一个非 NULL”的行数,语义完全不同。
- 每个
COUNT(字段)独立判断自己的 NULL 性,互不干扰 - 字段间有依赖关系(比如 discount 只在 price 存在时才有)?那得加 WHERE 或 CASE 过滤,COUNT 本身不处理逻辑约束
- PostgreSQL 支持
COUNT(*) FILTER (WHERE ...),但跨库兼容性差,优先用标准写法
SELECT category,
COUNT(price) AS non_null_price,
COUNT(stock) AS non_null_stock,
COUNT(discount) AS non_null_discount
FROM products
GROUP BY category;
遇到 COUNT 返回 0 却怀疑数据有问题?先查 GROUP BY 是否漏了 NULL 分组
如果某组本该有数据,但 COUNT(字段) 返回 0,第一反应不该是函数写错了,而是检查:这个分组键本身是不是 NULL?很多数据库(如 MySQL 默认模式)会把 GROUP BY column 中的 NULL 值聚合成单独一组;但如果你用了 WHERE column IS NOT NULL,那这一组就彻底消失了。
- 用
SELECT category, COUNT(*) FROM products GROUP BY category;看原始分组分布 - 特别留意
category为 NULL 的行是否被你无意过滤掉了 - MySQL 8.0+ 开启
sql_mode=STRICT_TRANS_TABLES后,GROUP BY 对 NULL 处理更明确,但老版本行为可能隐晦
真正容易被忽略的,是分组字段的 NULL 值是否参与了聚合——它不报错,也不提示,只是默默少了一行结果。











