searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

DISTINCT vs GROUP BY:去重场景下的性能实测与选型指南

2026-07-08 13:43:35
2
0

一、一个被问了无数次的问题

在 SQL 编写中,去重是最基础也最高频的操作之一。当你需要从一张表中提取不重复的字段值时,脑海里往往同时浮现两个关键字:DISTINCT 和 GROUP BY。

它们看起来殊途同归,都能把重复的行过滤掉,只留下唯一值。但在性能层面,两者的表现可能天差地别。很多开发者凭经验选一个,觉得"反正结果一样"。直到某天数据量暴涨,查询从毫秒级退化到秒级,才意识到当初那个随意的选择,埋下了一个性能隐患。

这篇文章,我们从执行计划、内部机制、多场景实测三个维度,把 DISTINCT 和 GROUP BY 在去重场景下的差异彻底拆开,给你一份可直接用于生产环境的选型指南。


二、先搞清底层:它们真的一样吗

从 SQL 语法的语义上看,DISTINCT 和 GROUP BY 在单纯去重时确实等价。但数据库引擎对它们的处理路径,存在根本性的差异。

DISTINCT 的执行逻辑

DISTINCT 的核心动作是"去重"。数据库引擎在扫描数据时,会维护一个哈希表(或排序结构),每读到一行就检查该行的去重字段是否已经存在。如果不存在,就保留;如果已存在,就丢弃。整个过程只关心"这个值出现过没有",不关心出现了几次,也不做任何聚合运算。

在执行计划中,DISTINCT 通常对应一个"去重"节点,有时也会被优化为"排序去重"或"哈希去重",取决于数据量和内存情况。

GROUP BY 的执行逻辑

GROUP BY 的核心动作是"分组"。它把相同值的行归到一组,然后对每一组执行聚合操作。即便你不写任何聚合函数,数据库引擎也会为每一组保留一条记录。这意味着 GROUP BY 在内部多做了一步:先分组,再从每组中选出一条代表。

在执行计划中,GROUP BY 通常对应一个"分组聚合"节点,涉及排序或哈希分组,以及分组后的数据提取。

关键差异就在这里:DISTINCT 只需要判断"是否出现过",而 GROUP BY 需要完成"分组 + 提取代表行"两步。多出来的这一步,在小数据量下几乎无感,但在大数据量下,可能成为性能分水岭。


三、实测设计:控制变量,逐项对比

为了得到客观结论,我们设计了三组典型场景,分别在不同数据规模下测试 DISTINCT 和 GROUP BY 的执行耗时。测试环境为单表,字段类型为整型和字符串,数据量分别设置为十万级、百万级、千万级。

场景一:单字段去重,无索引

这是最常见的情况:从一张大表中提取某个字段的唯一值。数据完全无序,没有任何索引辅助。

结果显示:在十万级数据时,两者耗时几乎一致,差距在百分之五以内。但到了百万级,DISTINCT 开始出现优势,平均快百分之八到十二。到了千万级,差距拉大到百分之十五到二十。DISTINCT 始终略胜一筹。

原因很直接:DISTINCT 的哈希去重在遇到重复值时可以立即丢弃,而 GROUP BY 即便不做聚合,也要先完成分组结构的构建,多了一层开销。

场景二:单字段去重,有索引

当去重字段上存在索引时,情况发生了变化。数据库可以直接沿索引树扫描,天然有序,去重变得极其高效。

在这个场景下,DISTINCT 和 GROUP BY 的耗时都大幅下降,但 GROUP BY 的降幅更大。原因在于:GROUP BY 可以利用索引的有序性直接完成分组,而 DISTINCT 虽然也能走索引,但其内部的去重判断逻辑在有序数据下并没有比 GROUP BY 省去多少步骤。两者差距缩小到百分之三以内,基本可以视为持平。

场景三:多字段组合去重

当去重条件涉及两个或以上字段时,比如按"城市 + 类别"组合去重,情况又有不同。

DISTINCT 需要对组合字段构建复合哈希键,内存占用上升。GROUP BY 同样需要复合分组,但其分组逻辑在多字段场景下与 DISTINCT 的差异进一步缩小。实测中,两者耗时几乎完全一致,差距不超过百分之二。

这说明:当去重维度增加时,DISTINCT 的轻量优势被稀释,两者趋于等价。


四、执行计划里的秘密:看懂这几个关键词

如果你不想跑实测,也可以通过执行计划快速判断该用哪个。关注以下几个关键词:

DISTINCT 对应的计划节点

通常出现"Unique"或"Distinct"字样。如果走哈希去重,会看到"Hash Aggregate";如果走排序去重,会看到"Sort + Unique"。哈希去重在数据量适中时效率更高,排序去重在数据已有序或内存不足时更稳定。

GROUP BY 对应的计划节点

通常出现"Group By"或"Aggregate"字样。如果不带聚合函数,执行计划中仍会有分组操作,但不会出现"Sum""Count"等聚合计算。如果你看到执行计划里有明确的聚合函数计算,说明引擎在做多余的工作——这时候换成 DISTINCT 会更优。

一个关键判断点:如果执行计划中 GROUP BY 的节点后跟着聚合计算(哪怕是计数),而你实际上不需要这个计数,那就果断换成 DISTINCT。这一步看似微小,在大表上可能节省可观的时间。


五、选型指南:什么时候用哪个

根据上述分析和实测结论,我们可以总结出一条清晰的选型路径:

优先选 DISTINCT 的场景

  • 单纯去重,不需要任何聚合信息
  • 单字段或少量字段去重
  • 数据量较大,且无合适索引
  • 对执行效率有较高要求

DISTINCT 语义清晰,执行路径更短,在大多数去重场景下是更优选择。它告诉数据库"我只要唯一值,别的不要",引擎会按最简洁的路径执行。

可以选 GROUP BY 的场景

  • 去重的同时需要聚合信息,比如去重后统计每组的数量
  • 多字段组合去重,且后续需要对每组做进一步处理
  • 去重字段有索引,且你习惯用 GROUP BY 的写法
  • 需要配合 HAVING 子句做分组后过滤

注意:如果你用 GROUP BY 仅仅是为了去重,而不需要任何聚合,这在语义上是一种"滥用"。虽然数据库能正确执行,但从代码可读性和执行效率两个角度,DISTINCT 都是更好的选择。

一个容易踩的坑

有些开发者会写出"SELECT 字段, COUNT() FROM 表 GROUP BY 字段"来实现去重加计数。如果你只需要去重,不需要计数,就不要写 COUNT()。这个多余的聚合操作会让执行计划多出一个计算步骤,在大数据量下影响明显。


六、进阶话题:去重之外的考量

除了性能,还有几个维度值得关注。

可读性与维护性

DISTINCT 的语义一目了然:去重。GROUP BY 的语义是分组,用它来去重属于"曲线救国"。当你的同事接手这段代码时,看到 DISTINCT 会立刻明白意图;看到 GROUP BY 则需要多想一步:"他是要分组还是要去重?"在团队协作中,语义明确的写法永远优于语义模糊的写法。

与窗口函数的配合

在复杂查询中,DISTINCT 和 GROUP BY 与窗口函数的交互方式不同。如果你的去重逻辑需要结合行号、排名等窗口计算,通常需要先用子查询或 CTE 做去重,再套窗口函数。这种情况下,用哪个关键字去重对最终性能影响不大,关键在于子查询的写法是否合理。

数据库引擎的优化差异

不同数据库引擎对 DISTINCT 和 GROUP BY 的优化策略不同。有些引擎会把 GROUP BY(不带聚合)自动重写为 DISTINCT,有些则不会。这意味着在某些数据库中,你写 GROUP BY 去重和写 DISTINCT 去重,最终执行计划可能完全一样。但你不能依赖这种隐式优化,因为换一个数据库,行为可能就变了。显式写出你的意图,才是可迁移、可预期的做法。

0条评论
0 / 1000
c****t
1019文章数
1粉丝数
c****t
1019 文章 | 1 粉丝
原创

DISTINCT vs GROUP BY:去重场景下的性能实测与选型指南

2026-07-08 13:43:35
2
0

一、一个被问了无数次的问题

在 SQL 编写中,去重是最基础也最高频的操作之一。当你需要从一张表中提取不重复的字段值时,脑海里往往同时浮现两个关键字:DISTINCT 和 GROUP BY。

它们看起来殊途同归,都能把重复的行过滤掉,只留下唯一值。但在性能层面,两者的表现可能天差地别。很多开发者凭经验选一个,觉得"反正结果一样"。直到某天数据量暴涨,查询从毫秒级退化到秒级,才意识到当初那个随意的选择,埋下了一个性能隐患。

这篇文章,我们从执行计划、内部机制、多场景实测三个维度,把 DISTINCT 和 GROUP BY 在去重场景下的差异彻底拆开,给你一份可直接用于生产环境的选型指南。


二、先搞清底层:它们真的一样吗

从 SQL 语法的语义上看,DISTINCT 和 GROUP BY 在单纯去重时确实等价。但数据库引擎对它们的处理路径,存在根本性的差异。

DISTINCT 的执行逻辑

DISTINCT 的核心动作是"去重"。数据库引擎在扫描数据时,会维护一个哈希表(或排序结构),每读到一行就检查该行的去重字段是否已经存在。如果不存在,就保留;如果已存在,就丢弃。整个过程只关心"这个值出现过没有",不关心出现了几次,也不做任何聚合运算。

在执行计划中,DISTINCT 通常对应一个"去重"节点,有时也会被优化为"排序去重"或"哈希去重",取决于数据量和内存情况。

GROUP BY 的执行逻辑

GROUP BY 的核心动作是"分组"。它把相同值的行归到一组,然后对每一组执行聚合操作。即便你不写任何聚合函数,数据库引擎也会为每一组保留一条记录。这意味着 GROUP BY 在内部多做了一步:先分组,再从每组中选出一条代表。

在执行计划中,GROUP BY 通常对应一个"分组聚合"节点,涉及排序或哈希分组,以及分组后的数据提取。

关键差异就在这里:DISTINCT 只需要判断"是否出现过",而 GROUP BY 需要完成"分组 + 提取代表行"两步。多出来的这一步,在小数据量下几乎无感,但在大数据量下,可能成为性能分水岭。


三、实测设计:控制变量,逐项对比

为了得到客观结论,我们设计了三组典型场景,分别在不同数据规模下测试 DISTINCT 和 GROUP BY 的执行耗时。测试环境为单表,字段类型为整型和字符串,数据量分别设置为十万级、百万级、千万级。

场景一:单字段去重,无索引

这是最常见的情况:从一张大表中提取某个字段的唯一值。数据完全无序,没有任何索引辅助。

结果显示:在十万级数据时,两者耗时几乎一致,差距在百分之五以内。但到了百万级,DISTINCT 开始出现优势,平均快百分之八到十二。到了千万级,差距拉大到百分之十五到二十。DISTINCT 始终略胜一筹。

原因很直接:DISTINCT 的哈希去重在遇到重复值时可以立即丢弃,而 GROUP BY 即便不做聚合,也要先完成分组结构的构建,多了一层开销。

场景二:单字段去重,有索引

当去重字段上存在索引时,情况发生了变化。数据库可以直接沿索引树扫描,天然有序,去重变得极其高效。

在这个场景下,DISTINCT 和 GROUP BY 的耗时都大幅下降,但 GROUP BY 的降幅更大。原因在于:GROUP BY 可以利用索引的有序性直接完成分组,而 DISTINCT 虽然也能走索引,但其内部的去重判断逻辑在有序数据下并没有比 GROUP BY 省去多少步骤。两者差距缩小到百分之三以内,基本可以视为持平。

场景三:多字段组合去重

当去重条件涉及两个或以上字段时,比如按"城市 + 类别"组合去重,情况又有不同。

DISTINCT 需要对组合字段构建复合哈希键,内存占用上升。GROUP BY 同样需要复合分组,但其分组逻辑在多字段场景下与 DISTINCT 的差异进一步缩小。实测中,两者耗时几乎完全一致,差距不超过百分之二。

这说明:当去重维度增加时,DISTINCT 的轻量优势被稀释,两者趋于等价。


四、执行计划里的秘密:看懂这几个关键词

如果你不想跑实测,也可以通过执行计划快速判断该用哪个。关注以下几个关键词:

DISTINCT 对应的计划节点

通常出现"Unique"或"Distinct"字样。如果走哈希去重,会看到"Hash Aggregate";如果走排序去重,会看到"Sort + Unique"。哈希去重在数据量适中时效率更高,排序去重在数据已有序或内存不足时更稳定。

GROUP BY 对应的计划节点

通常出现"Group By"或"Aggregate"字样。如果不带聚合函数,执行计划中仍会有分组操作,但不会出现"Sum""Count"等聚合计算。如果你看到执行计划里有明确的聚合函数计算,说明引擎在做多余的工作——这时候换成 DISTINCT 会更优。

一个关键判断点:如果执行计划中 GROUP BY 的节点后跟着聚合计算(哪怕是计数),而你实际上不需要这个计数,那就果断换成 DISTINCT。这一步看似微小,在大表上可能节省可观的时间。


五、选型指南:什么时候用哪个

根据上述分析和实测结论,我们可以总结出一条清晰的选型路径:

优先选 DISTINCT 的场景

  • 单纯去重,不需要任何聚合信息
  • 单字段或少量字段去重
  • 数据量较大,且无合适索引
  • 对执行效率有较高要求

DISTINCT 语义清晰,执行路径更短,在大多数去重场景下是更优选择。它告诉数据库"我只要唯一值,别的不要",引擎会按最简洁的路径执行。

可以选 GROUP BY 的场景

  • 去重的同时需要聚合信息,比如去重后统计每组的数量
  • 多字段组合去重,且后续需要对每组做进一步处理
  • 去重字段有索引,且你习惯用 GROUP BY 的写法
  • 需要配合 HAVING 子句做分组后过滤

注意:如果你用 GROUP BY 仅仅是为了去重,而不需要任何聚合,这在语义上是一种"滥用"。虽然数据库能正确执行,但从代码可读性和执行效率两个角度,DISTINCT 都是更好的选择。

一个容易踩的坑

有些开发者会写出"SELECT 字段, COUNT() FROM 表 GROUP BY 字段"来实现去重加计数。如果你只需要去重,不需要计数,就不要写 COUNT()。这个多余的聚合操作会让执行计划多出一个计算步骤,在大数据量下影响明显。


六、进阶话题:去重之外的考量

除了性能,还有几个维度值得关注。

可读性与维护性

DISTINCT 的语义一目了然:去重。GROUP BY 的语义是分组,用它来去重属于"曲线救国"。当你的同事接手这段代码时,看到 DISTINCT 会立刻明白意图;看到 GROUP BY 则需要多想一步:"他是要分组还是要去重?"在团队协作中,语义明确的写法永远优于语义模糊的写法。

与窗口函数的配合

在复杂查询中,DISTINCT 和 GROUP BY 与窗口函数的交互方式不同。如果你的去重逻辑需要结合行号、排名等窗口计算,通常需要先用子查询或 CTE 做去重,再套窗口函数。这种情况下,用哪个关键字去重对最终性能影响不大,关键在于子查询的写法是否合理。

数据库引擎的优化差异

不同数据库引擎对 DISTINCT 和 GROUP BY 的优化策略不同。有些引擎会把 GROUP BY(不带聚合)自动重写为 DISTINCT,有些则不会。这意味着在某些数据库中,你写 GROUP BY 去重和写 DISTINCT 去重,最终执行计划可能完全一样。但你不能依赖这种隐式优化,因为换一个数据库,行为可能就变了。显式写出你的意图,才是可迁移、可预期的做法。

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0