group by 后直接用 count(distinct column) 即可统计每组内该字段不重复值个数,主流sql引擎均支持;多列写法 count(distinct a,b) 是按组合去重,非分别去重;mysql 5.6及更早需子查询绕过;null默认被忽略,需coalesce显式处理;大数据量建议用近似函数优化性能。

GROUP BY 后直接用 COUNT(DISTINCT column) 就行
绝大多数主流 SQL 引擎(PostgreSQL、MySQL 5.7+、SQL Server、Oracle、SQLite 3.8.2+)都支持在 GROUP BY 中嵌套 COUNT(DISTINCT column),无需子查询或窗口函数。它会先按分组聚合,再对每组内去重计数。
常见错误是误写成 COUNT(DISTINCT column, other_column) —— 多列 DISTINCT 是按组合去重,不是分别去重。如果想统计多个字段各自的非重复数量,必须分开写:
COUNT(DISTINCT user_id), COUNT(DISTINCT product_id)
MySQL 5.6 及更早版本不支持 GROUP BY + COUNT(DISTINCT)
这类旧版 MySQL 会报错 Invalid use of group function,本质是解析器限制,不是语法错误。绕过方法只有两种:
- 升级 MySQL 到 5.7 或更高版本(推荐)
- 改用子查询:先
SELECT DISTINCT group_col, target_col FROM table去重,再外层GROUP BY group_col并COUNT(*)
注意子查询方式在大数据量下性能明显下降,因为无法利用原始表的索引做分组前裁剪。
COUNT(DISTINCT) 在 NULL 值上的行为要留意
COUNT(DISTINCT column) 默认忽略 NULL —— 这和 COUNT(*) 不同,也和 COUNT(column) 一致。如果你的业务逻辑中 NULL 代表一种有效状态(比如“未指定分类”),那它不会被计入结果。
需要把 NULL 当作独立值统计?得显式转换:
COUNT(DISTINCT COALESCE(category, 'NULL'))
或者用条件表达式构造占位符,避免和真实字符串冲突。
大数据量下 COUNT(DISTINCT) 可能触发内存溢出
PostgreSQL 和 Spark SQL 在处理高基数(high-cardinality)字段时,COUNT(DISTINCT) 默认走精确算法,会把所有值暂存内存。当单个分组内去重后仍有上百万值,容易 OOM。
应对策略取决于引擎:
- PostgreSQL:可改用
APPROX_COUNT_DISTINCT()(v13+)或扩展hyperloglog - Spark SQL:
approx_count_distinct()是默认推荐 - ClickHouse:原生
uniq()就是近似算法,精度够用且快
精确性要求不高时,近似函数通常快 3–10 倍,内存占用降为 1% 左右。










