COUNT(DISTINCT column) 返回0而非NULL,是因为它完全忽略NULL值——既不参与去重也不计入计数;整列全为NULL时结果为0,若需将NULL视为有效值,须用COALESCE转换。

为什么 COUNT(DISTINCT column) 返回 NULL 或 0?
常见原因是字段本身包含大量 NULL 值——COUNT(DISTINCT) 会忽略所有 NULL,如果整列都是 NULL,结果就是 0;如果误以为 NULL 会被当作一个独立值去重,就会困惑为什么没被计入。
-
COUNT(DISTINCT)永远不统计NULL,哪怕只有一行非空值,其余全为NULL,结果也是 1 - 想把
NULL当作一种“有效取值”参与去重?得先用COALESCE(column, 'NULL_PLACEHOLDER')转换 - MySQL 5.7+ 和 PostgreSQL 行为一致;但旧版 SQLite(COUNT(DISTINCT) 多列,单列没问题
COUNT(DISTINCT) 能不能对多列组合去重?
可以,但语法和效果容易误解:写成 COUNT(DISTINCT col1, col2) 是合法的(MySQL、PostgreSQL、SQL Server 支持),它统计的是 (col1, col2) 元组的唯一组合数,不是分别对两列去重再相加。
- 错误理解:
COUNT(DISTINCT a, b)≠COUNT(DISTINCT a) + COUNT(DISTINCT b) - 正确等价写法(兼容性更好):
COUNT(*)配合GROUP BY a, b子查询,但性能通常更差 - 注意:Oracle 不支持多列
DISTINCT直接写在COUNT()里,必须用子查询或GROUP BY
性能很慢?别直接套用 COUNT(DISTINCT)
在大表上执行 COUNT(DISTINCT) 往往触发全表扫描 + 内存哈希去重,尤其当去重字段无索引或基数极高时,响应可能从毫秒级跳到分钟级。
- 优先检查该字段是否有索引——B-tree 索引对
COUNT(DISTINCT)加速有限,但位图索引(如 PostgreSQL)或物化视图可缓解 - 数据量超百万行?考虑用近似函数替代,比如 PostgreSQL 的
APPROX_COUNT_DISTINCT(),误差率 - 如果只是判断“是否超过阈值”,可用
LIMIT n+1配合子查询提前终止,比算出精确值快得多
GROUP BY 后再 COUNT(DISTINCT) 容易漏掉空组
当配合 GROUP BY 使用时,COUNT(DISTINCT) 对每组单独计算,但如果某组没有任何非空值(例如全为 NULL),该组仍会出现在结果中,且对应值为 0 —— 这不是 bug,是符合 SQL 标准的行为。
- 想排除“空组”?加
HAVING COUNT(DISTINCT col) > 0过滤 - 需要显示“0 条记录”的组?确保
GROUP BY字段本身不为NULL,否则那行根本不会进分组(除非用GROUPING SETS) - MySQL 8.0+ 支持
GROUP BY ... WITH ROLLUP,但COUNT(DISTINCT)在 rollup 行表现不稳定,建议避免混用
NULL、空字符串、不可见字符,还有执行计划里那个没走索引的 Using temporary; Using filesort。

















