distinct与group by语义不同:前者去重返回,后者分组归约;无索引时distinct通常更快因哈希去重避免filesort,有索引时性能趋同但group by多列易退化;语义不等价时不可互换。

看执行计划前先明确:DISTINCT ≠ GROUP BY 的语义
哪怕只选一列、不加聚合函数,DISTINCT 和 GROUP BY 在 MySQL 内部处理逻辑也不同。前者目标是“去重后返回”,后者目标是“分组后归约”——哪怕你没写 COUNT(),优化器仍可能按分组流程走。这直接导致执行计划里出现 Using filesort 或额外的临时表。
为什么无索引时 DISTINCT 通常更快
在没有索引的列上做去重,MySQL 对 DISTINCT 常用哈希去重(Using temporary; Using filesort 不一定出现);而 GROUP BY 在 MySQL 8.0 之前默认触发排序,即使你加了 ORDER BY NULL,优化器也可能忽略或失效。
-
DISTINCT:扫描一次数据,边读边塞哈希表,重复键跳过 -
GROUP BY:先建临时表,再排序分组(尤其当sql_mode包含ONLY_FULL_GROUP_BY且字段未被函数依赖时更易触发) - 实测中,194 万行无索引表对单列去重,
DISTINCT平均快 1.3–1.7 倍,主要省在避免filesort
有索引时两者性能趋同,但仍有隐性风险
如果去重字段上有有效索引(B+Tree),DISTINCT 和 GROUP BY 都可能走索引扫描(type: index),这时执行时间几乎一样。但注意:
-
GROUP BY若涉及多列且索引不是最左前缀匹配,可能退化为全表扫描 + 临时表 -
DISTINCT在多列组合去重时,要求索引覆盖所有列才高效;否则仍要回表或建临时表 - MySQL 8.0+ 启用
group_by_optimization后,部分简单GROUP BY会被自动重写为DISTINCT,但不可依赖——得看EXPLAIN FORMAT=TREE输出是否含grouping_operation
别光看“快不快”,先看“能不能替代”
真正该纠结的不是性能,而是语义是否等价。比如:
- 你要的是
SELECT DISTINCT a, b FROM t→ 不能用GROUP BY a替代,会漏掉b不同但a相同的组合 - 你要的是
SELECT a, COUNT(*) FROM t GROUP BY a→DISTINCT根本没法实现 - 某些 ORM(如 Entity Framework Core)把
.Distinct()翻译成GROUP BY,结果在无索引场景下拖慢查询,这时得手动改写 SQL 或加索引
执行计划不是终点,是起点。同一个语句在不同版本、不同 sql_mode、不同索引状态下,EXPLAIN 结果可能完全不同。别跳过 FORMAT=TREE 和 STATUS 中的 Handler_read_* 计数。










