GROUP BY 中 NULL 默认归为一组,COUNT(*) 统计该组行数,COUNT(列名) 跳过 NULL;用 COALESCE 替换 NULL 可移植且需在 SELECT 和 GROUP BY 中同步使用;单独统计 NULL 应用 WHERE 或条件聚合;ORDER BY 时注意 COALESCE 类型隐式转换影响排序。

GROUP BY 中 NULL 值默认被归为同一组,但 COUNT(*) 会统计它,COUNT(列名) 不会
这是最容易混淆的点:在 GROUP BY col 时,所有 NULL 值会被聚合成一个分组;但 COUNT(col) 跳过 NULL,而 COUNT(*) 统计整行——包括 col 是 NULL 的那行。所以如果你写 COUNT(status),那一组 NULL 状态的记录数会显示为 0,不是你想要的“有多少条状态为空”。
用 COALESCE 把 NULL 转成占位符再分组,比 IFNULL 更通用
COALESCE 是 SQL 标准函数,所有主流数据库(MySQL、PostgreSQL、SQL Server、SQLite)都支持;IFNULL 是 MySQL 特有,PostgreSQL 会报错。想写可移植的 SQL,优先选 COALESCE。常见写法是把 NULL 映射成字符串如 '<null>'</null> 或数字 -1,确保它不和业务值冲突。
例如统计订单状态分布,把空状态标为 'unknown':
SELECT COALESCE(status, 'unknown') AS status_group, COUNT(*) AS cnt FROM orders GROUP BY COALESCE(status, 'unknown');
注意:这里必须在 SELECT 和 GROUP BY 里都写一遍 COALESCE(status, 'unknown'),不能只在 SELECT 里转换然后 GROUP BY status——那样 NULL 还是单独一组,且没被重命名。
想单独统计 NULL 行数?直接 WHERE 判断更清晰
如果目标只是知道“status 为 NULL 的订单有多少条”,没必要硬塞进 GROUP BY。加个条件查更直白:
SELECT COUNT(*) FROM orders WHERE status IS NULL;- 或者合并在主查询里用条件聚合:
SUM(CASE WHEN status IS NULL THEN 1 ELSE 0 END) AS null_count
混用 COALESCE + GROUP BY 再过滤,反而容易绕晕,尤其当还有其他非空分组逻辑时。
ORDER BY 里对 COALESCE 结果排序可能出意料
COALESCE(status, 'unknown') 返回的是字符串,如果原 status 是数字类型(比如 tinyint),MySQL 会隐式转成字符串排序,导致 '10' 排在 '2' 前面。这时要么统一转数字:COALESCE(CAST(status AS SIGNED), -1),要么在 ORDER BY 单独处理:ORDER BY (status IS NULL) DESC, status——先把 NULL 放最后,再按原值排。
真正麻烦的不是转换本身,而是不同数据库对 GROUP BY + COALESCE 的执行计划优化程度不一;PostgreSQL 可能多一次哈希计算,MySQL 8.0+ 通常能复用索引,但前提是 COALESCE 表达式没破坏索引字段的裸引用。

















