COUNT(DISTINCT column_name) 可统计每个分组中某字段的不同值个数,自动将NULL视为一个值;语法错误写法DISTINCT COUNT()不合法;大数据量时可建联合索引或使用近似函数优化。

GROUP BY 后统计每个分组里某字段有多少个不同值
直接用 COUNT(DISTINCT column_name) 就行,这是最常用也最直观的解法。它会在每个分组内部去重后再计数,不是全表去重。
常见错误是写成 DISTINCT COUNT(column_name) —— 这语法不合法,SQL 会报错 ERROR: syntax error at or near "COUNT"。
使用场景比如:查每个部门有多少种不同的职级,或每个订单号对应多少个不重复的商品类目。
-
COUNT(DISTINCT)在 PostgreSQL、MySQL 5.7+、SQL Server 2017+、Oracle、SQLite3 中都支持 - MySQL 5.6 或更早版本不支持该语法,得用子查询模拟
- 如果
column_name允许 NULL,DISTINCT会把所有 NULL 当作一个值处理(即多个 NULL 只算 1 次)
遇到 NULL 值时 COUNT(DISTINCT) 怎么算
COUNT(DISTINCT column_name) 默认忽略 NULL —— 准确说是把所有 NULL 视为同一值,并计入去重结果中,但只要该分组里有至少一个 NULL,它就只贡献 1 次计数。
如果你希望 NULL 完全不参与计数(即当某分组全是 NULL 时,结果为 0),得加条件过滤:
SELECT dept_id, COUNT(DISTINCT CASE WHEN job_level IS NOT NULL THEN job_level END) AS distinct_levels FROM employees GROUP BY dept_id;
注意这里不能写 COUNT(DISTINCT IF(job_level IS NOT NULL, job_level, NULL)),因为 MySQL 的 IF 返回 NULL 仍会被当作一个去重项;用 CASE 显式排除更可靠。
大数据量下 COUNT(DISTINCT) 很慢怎么办
本质是数据库要对每个分组做哈希去重或排序,数据量大时内存占用高、执行时间长,尤其在没有合适索引时。
- 给
GROUP BY字段和DISTINCT字段联合建索引,例如(dept_id, job_level) - PostgreSQL 可考虑用
approx_count_distinct()(需安装hyperloglog扩展)换取速度,误差率约 1% - MySQL 8.0+ 支持窗口函数,但
COUNT(DISTINCT) OVER(...)仍受限于引擎实现,部分版本不支持,执行前先试SELECT COUNT(DISTINCT x) OVER(PARTITION BY y) FROM t;
想同时统计总数和不重复数,能不能只扫一遍表
可以,现代 SQL 引擎基本都支持在同一查询中混用聚合函数:
SELECT category, COUNT(*) AS total_items, COUNT(DISTINCT user_id) AS unique_users FROM orders GROUP BY category;
只要 GROUP BY 逻辑一致,数据库会一次扫描完成所有聚合,不会额外增加 I/O。但注意:某些旧版 Hive 或 Presto 对多 DISTINCT 的优化较差,可能触发多次 shuffle —— 这时候得看执行计划里的 EXPLAIN 输出是否有重复的 Distinct 节点。
真正容易被忽略的是:当你要对同一个字段既做 COUNT(DISTINCT) 又做 GROUP_CONCAT(DISTINCT)(MySQL)或 STRING_AGG(DISTINCT ...)(PostgreSQL),部分引擎不支持在一条语句里对同一列多次用 DISTINCT,会报错或静默失效。

















