GROUP BY可以理解为 “按某个规则把数据分成小组,再对每个小组做汇总”—— 比如把 “所有订单按顾客分组”,统计每个顾客的总消费;而DISTINCT是 “直接去掉重复数据,只保留唯一值”。下面从基础用法到深层对比,带你彻底搞懂两者的区别和适用场景。
一、GROUP BY 的基础概念与作用
1. GROUP BY 的核心作用
GROUP BY的字面意思是 “按... 分组”,功能是:将表中的数据按指定字段(或字段组合)的取值分成多个 “组”,同一组内的字段取值完全相同,然后对每个组单独使用聚合函数(如COUNT、SUM、AVG等)进行汇总计算。
核心逻辑:“先分组,后聚合”
2. 基础语法结构
SELECT 分组字段, 聚合函数(字段) AS 别名
FROM 表名
[WHERE 筛选条件] -- 分组前筛选原始数据
GROUP BY 分组字段
[HAVING 聚合条件]; -- 分组后筛选汇总结果
3. 用实例理解:分组 + 聚合
还是用 “订单表(orders)” 为例,表中数据如下:
| order_id | user_id(顾客编号) | amount(金额) | create_time(下单时间) |
| 1 | 101 | 50 | 2025-05-01 |
| 2 | 102 | 150 | 2025-05-01 |
| 3 | 101 | 80 | 2025-05-02 |
| 4 | 103 | 200 | 2025-05-02 |
| 5 | 102 | 120 | 2025-05-03 |
实例 1:按 “顾客编号(user_id)” 分组,统计每个顾客的订单数和总消费
需求:想知道每个顾客下了多少笔订单,一共花了多少钱。
SELECT
user_id AS 顾客编号, -- 分组字段
COUNT(*) AS 订单总数, -- 聚合函数:统计每个小组的订单数
SUM(amount) AS 总消费 -- 聚合函数:计算每个小组的总金额
FROM orders
GROUP BY user_id; -- 按顾客编号分组
- 分组过程:把
user_id相同的记录归为一组(101 一组、102 一组、103 一组); - 聚合计算:对每个组算 “订单数” 和 “总消费”;
- 最终结果:
| 顾客编号 | 订单总数 | 总消费 |
| 101 | 2 | 130 |(订单 1+3:50+80) |
| 102 | 2 | 270 |(订单 2+5:150+120) |
| 103 | 1 | 200 |(订单 4) |
实例 2:按 “日期(create_time)” 分组,筛选并统计每天的有效订单
需求:只看 5 月 2 日及之后的订单,按日期分组统计 “金额≥100 的订单数”。
SELECT
create_time AS 下单日期,
COUNT(CASE WHEN amount >= 100 THEN 1 END) AS 大额订单数 -- 条件聚合
FROM orders
WHERE create_time >= '2025-05-02' -- 分组前筛选:只留5.2及之后的订单
GROUP BY create_time
HAVING 大额订单数 > 0; -- 分组后筛选:只留大额订单数>0的日期
结果:
| 下单日期 | 大额订单数 |
| 2025-05-02 | 1 |(订单 4 金额 200≥100) |
| 2025-05-03 | 1 |(订单 5 金额 120≥100) |
4. GROUP BY 的关键规则(小白必记)
- 分组字段要完整:如果按多个字段分组(比如 “日期 + 顾客”),
GROUP BY后要写所有分组字段,SELECT也要对应。例:按 “日期 + 顾客” 分组,统计每天每个顾客的订单数:
SELECT create_time, user_id, COUNT(*) AS 当日订单数
FROM orders
GROUP BY create_time, user_id; -- 两个分组字段都要写
- WHERE 和 HAVING 的区别:
WHERE管 “分组前”(筛选原始数据),HAVING管 “分组后”(筛选汇总结果),HAVING可以用聚合函数,WHERE不能。 - NULL 的处理:分组字段为 NULL 的记录会被归为 “同一个小组”(比如多个
user_id为 NULL 的记录,会分成一组)。
二、GROUP BY 与 DISTINCT 的去重对比
DISTINCT的作用是 “去掉重复记录,只保留所有字段都相同的唯一记录”,核心是 “单纯去重,不做汇总”。很多小白会混淆两者,下面从 “功能、用法、场景” 三个维度对比:
1. 核心区别:“去重 + 汇总” vs “单纯去重”
| 对比维度 | GROUP BY | DISTINCT |
| 核心功能 | 先分组,再对每组做聚合计算(统计、求和等) | 直接去掉完全重复的记录,只保留唯一值 |
| 是否依赖聚合函数 | 必须配合聚合函数(否则意义不大) | 不依赖任何函数,单独使用 |
| 结果含义 | 每个小组的汇总效果(如“每个顾客的总消费”) | 去重后的原始记录(如“所有不重复的顾客编号”) |
| 执行逻辑 | 分组->聚合计算 | 扫描记录->去重->返回唯一值 |
2. 实例对比:同样是 “获取不重复的顾客编号”
用 DISTINCT 实现(单纯去重)
需求:只想知道 “有哪些顾客下过单”(不需要统计订单数)。
SELECT DISTINCT user_id AS 不重复顾客编号
FROM orders;
- 结果(只保留唯一的
user_id):
| 不重复顾客编号 |
| 101 |
| 102 |
| 103 |
- 逻辑:扫描所有
user_id,去掉重复的,直接返回唯一值。
用 GROUP BY 实现(分组 + 无意义聚合)
如果硬要用GROUP BY实现 “去重顾客编号”,需要配合无实际意义的聚合函数(比如COUNT(*),但结果会多一列无关的统计值):
SELECT user_id AS 不重复顾客编号, COUNT(*) AS 订单数 -- 必须加聚合函数
FROM orders
GROUP BY user_id;
- 结果(比 DISTINCT 多了 “订单数” 列):
| 不重复顾客编号 | 订单数 |
| 101 | 2 |
| 102 | 2 |
| 103 | 1 |
- 问题:如果只需要 “不重复的顾客编号”,用
GROUP BY会多做一次聚合计算,效率低且结果冗余。
3. 实例对比:“统计不重复顾客数”
用 DISTINCT+COUNT 实现
需求:想知道 “一共有多少个不同的顾客下过单”。
SELECT COUNT(DISTINCT user_id) AS 不重复顾客总数
FROM orders;
- 结果:
不重复顾客总数 = 3(直接统计去重后的user_id数量)。
用 GROUP BY+COUNT 实现
先按user_id分组,再统计 “小组的数量”(需要嵌套子查询):
SELECT COUNT(*) AS 不重复顾客总数
FROM (
-- 子查询:按user_id分组,得到每个小组的user_id
SELECT user_id
FROM orders
GROUP BY user_id
) AS 分组结果;
- 结果同样是
3,但逻辑更复杂:先分组得到 3 个小组,再统计小组数量。 - 问题:比
DISTINCT+COUNT多了一层子查询,执行效率更低,代码更繁琐。
4. 什么时候用 GROUP BY?什么时候用 DISTINCT?
优先用 DISTINCT 的场景
- 只需要 “去掉重复记录”,不需要任何汇总计算(如 “获取所有不重复的商品分类”“获取所有不重复的下单日期”);
- 统计 “不重复值的数量”(如 “统计不重复的顾客数”“统计不重复的订单日期数”),用
COUNT(DISTINCT 字段)更简洁。
必须用 GROUP BY 的场景
- 需要 “按分组做汇总计算”(如 “每个顾客的总消费”“每天的订单总数”“每个商品分类的平均价格”);
- 需要 “分组后筛选汇总结果”(如 “总消费超过 100 元的顾客”“订单数超过 1 笔的日期”),必须用
GROUP BY+HAVING。
5. 性能对比:谁更快?
- 当只需要 “单纯去重” 或 “统计不重复值数量” 时:
DISTINCT比GROUP BY更快,因为GROUP BY会多做一次分组和聚合计算; - 当需要 “分组 + 汇总” 时:只能用
GROUP BY,DISTINCT无法实现; - 特殊情况:如果表中存在大量重复记录,
GROUP BY和DISTINCT的效率差异可能很小(现代数据库会自动优化),但优先选择逻辑更简洁的语法。
三、常见误区(小白避坑)
-
误区 1:GROUP BY 可以替代 DISTINCT 做单纯去重错!虽然 GROUP BY 能间接得到唯一值,但会多做聚合计算,效率低且代码冗余,单纯去重就用 DISTINCT。
-
误区 2:SELECT 里可以写非分组、非聚合的字段错!比如 “按 user_id 分组,SELECT 里写 order_id”,会报错(因为一个小组有多个 order_id,数据库不知道返回哪一个)。
-
误区 3:HAVING 可以替代 WHERE错!HAVING 是 “分组后筛选”,只能用聚合函数;WHERE 是 “分组前筛选”,不能用聚合函数。比如 “筛选金额≥100 的订单再分组”,必须用 WHERE,不能用 HAVING。
-
误区 4:GROUP BY 对多个字段分组时,顺序无关错!分组顺序会影响中间过程(但最终结果的字段值顺序不影响),比如 “按日期 + 顾客” 分组和 “按顾客 + 日期” 分组,分组逻辑不同,但最终结果的汇总值一致。
四、总结
| 场景需求 | 推荐语法 | 核心逻辑 |
| 单纯去重(如不重复的顾客编号) | SELECT DISTINCT 字段 FROM 表 | 去重→返回唯一值 |
| 统计不重复值数量(如顾客总数) | SELECT COUNT (DISTINCT 字段) | 去重→计数 |
| 按分组做汇总(如每个顾客的总消费) | SELECT 分组字段,聚合函数 FROM 表 GROUP BY 分组字段 | 分组→聚合计算 |
| 分组后筛选汇总结果(如总消费 > 100 的顾客) | GROUP BY + HAVING 聚合条件 | 分组→聚合→筛选汇总结果 |
记住:GROUP BY的核心是 “分组 + 汇总”,DISTINCT的核心是 “单纯去重”,不要用错场景。刚开始可以先记住 “需要统计、求和就用 GROUP BY,只需要去重就用 DISTINCT”,多练几次就能熟练区分。
939

被折叠的 条评论
为什么被折叠?



