GROUP BY 和 DISTINCT 是 SQL 中用于去重的两种方式,但它们的核心用途、底层原理和性能表现差异显著。以下是详细对比:
1. 核心用途差异
| 功能 | GROUP BY | DISTINCT |
|---|---|---|
| 主要目的 | 分组聚合(如计算每组的 SUM、COUNT) |
返回结果集的唯一行(去重) |
| 聚合函数 | 必须搭配聚合函数(如 SUM、AVG) |
不支持聚合函数,仅返回原始字段 |
| 示例 | GROUP BY dept_id + SUM(salary) |
DISTINCT city |
2. 底层执行原理
GROUP BY 的执行流程
- 分组阶段 :
- 按照
GROUP BY指定的字段对结果集进行排序(如果未使用索引)。 - 将相同值的行放入同一个组中。
- 按照
- 聚合阶段 :
- 对每个组应用聚合函数(如
SUM、COUNT),生成单行结果。
- 对每个组应用聚合函数(如
- 索引优化 :
- 如果
GROUP BY字段有索引,数据库会直接利用索引的有序性快速分组(避免排序)。 - 例如,MySQL 的
filesort操作可被索引避免。
- 如果
DISTINCT 的执行流程
- 排序/哈希去重 :
- 排序法:对结果集按所有选择的字段排序,相邻重复行只保留一行。
- 哈希法:构建哈希表,遍历结果集时丢弃哈希冲突的重复行。
- 索引优化 :
- 如果
SELECT的字段包含索引,数据库直接扫描索引(无需回表)。 - 例如,
SELECT DISTINCT email可通过email索引快速去重。
- 如果
3. 性能对比
| 场景 | GROUP BY | DISTINCT |
|---|---|---|
| 无聚合的简单去重 | 需额外排序开销,性能较差 | 专为去重优化,性能更好 |
| 带聚合函数的分组 | 直接分组聚合,性能最优 | 无法实现,必须用 GROUP BY |
| 索引优化 | 依赖索引避免排序(如覆盖索引) | 依赖索引避免回表(如唯一索引) |
| 大数据量去重 | 可能触发磁盘临时文件(排序溢出) | 哈希法更高效(内存足够时) |
4. 示例对比
假设有表 orders:
user_id |
product |
amount |
|---|---|---|
| 1 | A | 100 |
| 1 | B | 200 |
| 2 | A | 150 |
场景1:统计每个用户的总金额(聚合)
sql
SELECT user_id, SUM(amount) -- 必须用 GROUP BY
FROM orders
GROUP BY user_id;
-
结果 :
user_idSUM(amount)1 300 2 150
场景2:列出所有购买过的产品(去重)
sql
SELECT DISTINCT product; -- 简洁写法
-- 等价于(但性能更差):
SELECT product FROM orders GROUP BY product;
-
结果 :
productA B
5. 进阶区别
-
NULL 处理:
DISTINCT将所有NULL视为相同值(只保留一个)。GROUP BY同样将NULL分为一组(如果字段允许为NULL)。
-
多字段组合:
DISTINCT col1, col2去重的是两个字段的组合值。GROUP BY col1, col2对组合字段分组,需搭配聚合函数。
-
与
ORDER BY的联用:DISTINCT先去重,再按结果排序。GROUP BY先分组,聚合后按结果排序(索引可优化排序)。
6. 选择建议
| 需求 | 最佳选择 | 理由 |
|---|---|---|
| 简单去重(无聚合) | DISTINCT |
语法简洁,性能优化更好 |
| 分组聚合(如统计、平均值) | GROUP BY |
必须搭配聚合函数 |
| 多字段去重 + 排序 | DISTINCT + 索引 |
利用索引避免排序 |
| 大数据量分组 | GROUP BY + 索引 |
索引可避免 filesort |
总结
- GROUP BY 是为聚合而生,必须搭配聚合函数,底层通过排序或哈希实现分组。
- DISTINCT 是纯粹的去重工具,直接丢弃重复行,语法更简洁。
- 性能关键:合理使用索引(覆盖索引、唯一索引)可大幅提升两者效率。