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_id
SUM(amount)
1 300 2 150
场景2:列出所有购买过的产品(去重)
sql
SELECT DISTINCT product; -- 简洁写法
-- 等价于(但性能更差):
SELECT product FROM orders GROUP BY product;
-
结果 :
product
A 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 是纯粹的去重工具,直接丢弃重复行,语法更简洁。
- 性能关键:合理使用索引(覆盖索引、唯一索引)可大幅提升两者效率。