DISTINCT和GROUP BY在简单去重时执行计划常相同,性能差异取决于索引与数据分布;但GROUP BY支持聚合计算,DISTINCT不支持,且GROUP BY更易利用索引优化。

MySQL 中 DISTINCT 和 GROUP BY 实际执行计划往往相同
在绝大多数简单查询场景下,MySQL 优化器会将 SELECT DISTINCT col 和 SELECT col FROM ... GROUP BY col 视为等价操作,最终生成几乎一致的执行计划(比如都走 Using temporary; Using filesort 或都利用索引去重)。这意味着二者性能差异通常可以忽略——不是语法不同导致快慢,而是底层是否能走索引、数据分布、临时表大小决定实际耗时。
验证方式很简单:对同一语句分别加 EXPLAIN,观察 type、Extra 字段是否一致。例如:
EXPLAIN SELECT DISTINCT user_id FROM orders;
EXPLAIN SELECT user_id FROM orders GROUP BY user_id;
若两者 Extra 都含 Using index for group-by,说明都命中了覆盖索引;若都出现 Using temporary,则都触发了内存/磁盘临时表。
GROUP BY 在聚合计算时无法被 DISTINCT 替代
一旦需要统计信息(如计数、求和、最大值),GROUP BY 是唯一合法且高效的选择。DISTINCT 只能去重,不能配合 COUNT()、SUM() 等聚合函数做分组级计算。
-
SELECT user_id, COUNT(*) FROM orders GROUP BY user_id✅ 合法,返回每个用户订单数 -
SELECT DISTINCT user_id, COUNT(*) FROM orders❌ 报错:Invalid use of group function -
SELECT DISTINCT user_id FROM orders✅ 合法,但拿不到订单数量
强行用子查询模拟(如先 DISTINCT 再关联查总数)会导致多次扫描、无索引关联、性能断崖式下降,尤其在大表上不可接受。
有索引时,GROUP BY 更容易利用索引优化
MySQL 对 GROUP BY 的索引利用更积极,尤其是当 GROUP BY 字段是联合索引最左前缀时,可能直接避免排序和临时表;而 DISTINCT 在某些旧版本(如 5.7 之前)或复杂嵌套中,优化器未必能识别出等价索引路径。
例如表 orders(user_id, status, created_at) 有联合索引 (user_id, status):
-
SELECT DISTINCT user_id FROM orders—— 可能走索引,但不一定用上index for group-by优化 -
SELECT user_id FROM orders GROUP BY user_id—— 明确触发Using index for group-by,跳过排序步骤
这种差异在 ORDER BY 与 GROUP BY 字段一致时更明显,MySQL 可直接复用索引顺序输出结果。
写法选择应优先看语义,而非臆测性能
如果你只需要去重,用 DISTINCT 更直观;如果要分组聚合,必须用 GROUP BY。不要因为听说“GROUP BY 慢”就硬改写成 DISTINCT 加子查询——那反而引入更多 I/O 和内存开销。
真正影响性能的是这几点:
- 目标字段是否有合适索引(单列 or 联合索引,顺序是否匹配)
- 结果集大小:10 行去重和 100 万行去重,临时表是否溢出到磁盘
- 是否带
WHERE条件:过滤越早,参与去重/分组的数据越少 - MySQL 版本:8.0+ 对哈希聚合、并行排序支持更好,
GROUP BY整体更稳
最常被忽略的是:没加索引时,二者都会变慢,但慢的方式不同——DISTINCT 容易让人误以为“只是去个重而已”,结果全表扫完再内存去重;而 GROUP BY 报错或警告更早暴露问题。











