COUNT(DISTINCT col) 更优,因其语义精准、无需临时表、可利用索引加速;而 SUM/AVG(DISTINCT col) 易受精度和NULL影响,多列去重统计需子查询,且仅部分聚合函数支持 DISTINCT。

想对某列去重后再统计,别硬套 GROUP BY —— 直接用聚合函数自带的 DISTINCT 修饰符更简洁、语义更准、执行计划也更可控。
为什么 COUNT(DISTINCT col) 比 GROUP BY + COUNT(*) 更合适?
当你只关心「某一列有多少个不同值」,比如统计订单表里有多少个不重复的 customer_id,COUNT(DISTINCT customer_id) 是最直接的写法。它在引擎层就做了去重计数,不需要临时表、不生成中间分组结果。
-
GROUP BY customer_id; SELECT COUNT(*) FROM (SELECT DISTINCT customer_id FROM orders) t;多了一层子查询,MySQL 可能无法下推优化,尤其数据量大时容易触发磁盘临时表 -
COUNT(DISTINCT customer_id)由优化器内建支持,InnoDB 可利用索引 B+ 树顺序扫描加速(如果customer_id有索引) - 注意:
COUNT(DISTINCT)在旧版本 MySQL(如 5.6)中可能禁用 Loose Index Scan,导致性能陡降;8.0+ 默认启用且支持并行聚合
SUM(DISTINCT col) 和 AVG(DISTINCT col) 的实际约束
这两个函数看似合理,但真实场景中极易踩坑 —— 它们不是「先去重再求和」的业务逻辑代理,而是严格按 SQL 标准对表达式结果去重后计算。一旦 col 存在 NULL 或类型隐式转换,行为会出人意料。
-
SUM(DISTINCT salary)对salary列所有非NULL值去重后加总,但若该列是DECIMAL类型,浮点精度可能导致本应相等的值被判定为不同(例如100.00vs100.000) -
AVG(DISTINCT col)等价于SUM(DISTINCT col) / COUNT(DISTINCT col),分子分母都基于同一去重集合,但不会自动过滤掉业务上“无效的异常值”(比如salary = 0或负数) - 如果你的真实需求是「每个客户只算一次最高单笔金额的总和」,那
SUM(DISTINCT)完全不适用,得用窗口函数或子查询
多列组合去重统计只能靠子查询
COUNT(DISTINCT col1, col2) 是合法语法,MySQL 支持,但它统计的是 (col1, col2) 这一对值的唯一组合数。而如果你要的是「先按 col1 分组,再对每组内 col2 去重求和」,SUM(DISTINCT) 无能为力。
- 错误直觉:
SELECT col1, SUM(DISTINCT col2) FROM t GROUP BY col1—— 这句会报错,因为SUM(DISTINCT col2)不是标量聚合,不能与非聚合字段col1共存于同一SELECT - 正确做法:用子查询先去重,再外层分组,例如:
SELECT col1, SUM(col2) FROM (SELECT DISTINCT col1, col2 FROM t) AS dedup GROUP BY col1;
- 性能提示:这种写法在大数据量下可能显著变慢,建议提前在
(col1, col2)上建联合索引,让DISTINCT走索引覆盖扫描
DISTINCT 修饰符不支持所有聚合函数
只有 COUNT、SUM、AVG、MAX、MIN 这五个聚合函数允许接 DISTINCT;像 GROUP_CONCAT、JSON_AGG(MySQL 8.0.17+)、STDDEV 等都不支持。
-
COUNT(DISTINCT col)✅ 常用,安全 -
GROUP_CONCAT(DISTINCT col)✅ MySQL 扩展语法,可用 -
STDDEV(DISTINCT col)❌ 语法错误,必须先用子查询去重再调用 - 特别注意:
GROUP_CONCAT的DISTINCT会改变排序行为 —— 去重后的结果不再保证原始插入顺序,除非显式加ORDER BY
真正容易被忽略的点是:DISTINCT 在聚合函数里的语义,和它在 SELECT DISTINCT 中的语义并不等价。前者作用于函数输入集,后者作用于最终输出行;混用时逻辑边界一旦模糊,结果就不可信。别图省事跳过验证步骤。











