count(distinct user_id)是group by后统计每组不同用户数的最直接解法,它去重计数,而count(*)统计所有行、count(user_id)仅忽略null不解决重复问题。

GROUP BY 后用 COUNT(DISTINCT user_id) 是最直接解法
多数场景下,你要的不是“每组有多少行”,而是“每组有多少个不同用户”。COUNT(*) 会统计所有行,包含重复 user_id;必须用 COUNT(DISTINCT user_id) 才能去重计数。
常见错误是写成 COUNT(user_id) —— 这只是忽略 NULL 的行数,不解决重复问题。
- MySQL、PostgreSQL、SQL Server(2017+)、Oracle 都支持
COUNT(DISTINCT ...) - SQLite 支持,但性能较差,大数据量时可能卡住
- 旧版 SQL Server(2016 及之前)不支持在窗口函数里用
DISTINCT,但普通GROUP BY没问题 - 示例:
SELECT category, COUNT(DISTINCT user_id) AS unique_users<br>FROM orders<br>GROUP BY category;
遇到 NULL 用户 ID 时要小心
user_id 字段如果有 NULL,COUNT(DISTINCT user_id) 会自动跳过它——这是标准行为,不是 bug。但如果你业务上把 NULL 当作“未知用户”并想单独计数,就得额外处理。
- 想把
NULL算作一个独立用户:用COUNT(DISTINCT COALESCE(user_id, -1))或类似技巧 - 想单独统计
NULL数量:加一列COUNT(*) - COUNT(user_id) - 检查数据质量:先跑
SELECT COUNT(*), COUNT(user_id), COUNT(DISTINCT user_id) FROM table,对比三者差值
大数据量时 DISTINCT 计算慢怎么办
当表有千万级以上记录、分组又多时,COUNT(DISTINCT) 可能触发临时磁盘排序或哈希表膨胀,查询变慢甚至 OOM。
- 优先加复合索引:
CREATE INDEX idx_group_user ON table (group_col, user_id),让扫描更高效 - 如果允许近似结果(比如报表类场景),PostgreSQL 可用
APPROX_COUNT_DISTINCT(user_id),ClickHouse 用uniq(user_id) - 避免在子查询里嵌套多层
COUNT(DISTINCT),容易引发重复计算;可先用DISTINCT group_col, user_id去重再聚合
替代方案:用子查询 + COUNT 避免 DISTINCT 语法限制
某些数据库(如老版本 Hive、部分 OLAP 引擎)对 COUNT(DISTINCT) 有严格限制,或不支持在某些上下文使用。这时可以改用去重后计数的逻辑。
- 写法:
SELECT group_col, COUNT(*) AS unique_users<br>FROM (SELECT DISTINCT group_col, user_id FROM table) t<br>GROUP BY group_col;
- 注意:这个写法在
user_id为空或存在大量重复时,中间结果集可能比原表还大,内存压力反而更高 - 如果
group_col和user_id都是字符串且很长,建议先用HASH(user_id)转成整型再DISTINCT,减少传输开销
实际执行前,先看执行计划里的 Distinct 步骤是否走索引、是否 spill 到磁盘——这才是影响快慢的关键,不是语法本身。











