SQL GROUP BY 和聚合函数:汇总你的数据
Published: 2026-04-08
GROUP BY 会把在一个或多个列中具有相同值的多行折叠为一行汇总结果。配合 COUNT、SUM、AVG、MIN 和 MAX 等聚合函数,它构成了几乎所有报表和分析查询的基础。所有 SQL 数据库都支持 GROUP BY,包括 SQLite、PostgreSQL、MySQL、SQL Server 和 Oracle。
示例数据
下面所有示例都使用两张表:
employees 表:
| id | name | department | hire_date | salary |
|---|---|---|---|---|
| 1 | Alice | Engineering | 2023-01-15 | 95000 |
| 2 | Bob | Engineering | 2023-06-01 | 85000 |
| 3 | Carol | Engineering | 2024-03-10 | 85000 |
| 4 | Dave | Sales | 2023-02-20 | 72000 |
| 5 | Eve | Sales | 2023-09-05 | 68000 |
| 6 | Frank | Sales | 2024-01-15 | 65000 |
| 7 | Grace | Marketing | 2023-04-01 | 82000 |
| 8 | Henry | Marketing | 2024-06-01 | 70000 |
orders 表:
| id | customer | product | category | quantity | unit_price | order_date | status |
|---|---|---|---|---|---|---|---|
| 1 | Acme Corp | Laptop Pro | Electronics | 3 | 1299.99 | 2026-01-05 | delivered |
| 2 | Acme Corp | Wireless Mouse | Electronics | 10 | 29.99 | 2026-01-12 | delivered |
| 3 | Globex | Office Chair | Furniture | 5 | 249.99 | 2026-01-18 | delivered |
| 4 | Globex | Standing Desk | Furniture | 2 | 599.99 | 2026-01-20 | shipped |
| 5 | Initech | Notebook Pack | Stationery | 20 | 12.99 | 2026-02-03 | delivered |
| 6 | Initech | Pen Set | Stationery | 15 | 8.99 | 2026-02-07 | delivered |
| 7 | Acme Corp | Monitor 27" | Electronics | 4 | 399.99 | 2026-02-14 | delivered |
| 8 | Umbrella Ltd | Laptop Pro | Electronics | 2 | 1299.99 | 2026-02-20 | cancelled |
| 9 | Umbrella Ltd | Desk Lamp | Furniture | 8 | 39.99 | 2026-03-01 | delivered |
| 10 | Globex | Wireless Mouse | Electronics | 12 | 29.99 | 2026-03-05 | shipped |
| 11 | Initech | Monitor 27" | Electronics | 1 | 399.99 | 2026-03-10 | delivered |
| 12 | Acme Corp | Standing Desk | Furniture | 1 | 599.99 | 2026-03-15 | processing |
GROUP BY 语法
SELECT column1, aggregate_function(column2)
FROM table_name
WHERE condition
GROUP BY column1
HAVING aggregate_condition
ORDER BY column1;
- GROUP BY —— 一个或多个列,这些列的不同取值组合定义了每个分组
- 聚合函数 —— 从组内所有行计算出一个单一值
- HAVING —— 过滤分组(类似 WHERE,但作用于分组结果)
- SELECT 中所有不在聚合函数里的列,都必须出现在 GROUP BY 中
SQL 执行顺序
SQL 子句的书写顺序与执行顺序不同。理解这一点有助于解释为什么 HAVING 可以引用聚合函数而 WHERE 不行,以及为什么不能在 HAVING 子句中使用 SELECT 的别名。
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
| 步骤 | 子句 | 发生了什么 |
|---|---|---|
| 1 | FROM | 读取表并计算所有 JOIN |
| 2 | WHERE | 过滤单行 —— 这里不允许使用聚合函数 |
| 3 | GROUP BY | 将剩余行折叠为分组 |
| 4 | HAVING | 过滤分组 —— 这里允许使用聚合函数 |
| 5 | SELECT | 计算输出列和别名 |
| 6 | ORDER BY | 对最终结果集排序 |
因此,WHERE status = 'delivered' 在分组之前执行(快速的行级过滤),而 HAVING COUNT(*) > 2 在分组之后执行(过滤整个分组)。
COUNT()
COUNT(*) 统计组内所有行数。COUNT(column) 只统计该列中非 NULL 的值。
示例:每个部门的员工数量
示例:每个客户的订单数量
示例:已送达与其他状态的数量对比
使用带 CASE 表达式的 COUNT 只统计满足条件的订单:
SUM()
SUM(column) 对组内某个数值列的所有值求和。
示例:每个部门的工资总额
示例:每个品类的总收入
将收入计算为 quantity * unit_price:
示例:按客户和品类拆分收入
按多个列分组,以进一步细分收入:
AVG()
AVG(column) 返回组内所有非 NULL 值的算术平均数。
示例:每个部门的平均工资
示例:每个客户的平均订单金额
MIN() 和 MAX()
MIN(column) 和 MAX(column) 分别返回组内最小值和最大值。
示例:每个部门的工资区间
示例:每个客户的首单和末单日期
组合多个聚合函数
你可以在一条查询中组合任意数量的聚合函数。
示例:完整的部门汇总
示例:每个客户的订单汇总
HAVING
HAVING 在聚合之后过滤分组 —— 它是针对分组结果的 WHERE 等价物。
示例:员工数大于 2 的部门
示例:总收入超过 2000 的客户
示例:平均订单金额低于 500 的品类
WHERE 与 HAVING 的区别
WHERE 在分组之前过滤单行。HAVING 在聚合之后过滤分组。
示例:每个客户仅统计已送达订单的收入
使用 WHERE 在分组前排除行,再用 HAVING 过滤结果:
带 JOIN 的 GROUP BY
你可以先对多张表做连接,然后对连接后的数据进行分组。
示例:通过员工表关联统计每个部门的收入
示例:按品类和月份统计订单数量与收入
带表达式的 GROUP BY
你可以按计算表达式分组,而不仅仅是按原始列分组。
示例:每年入职的员工数量
示例:按月份统计订单
COUNT(DISTINCT)
COUNT(DISTINCT column) 统计组内唯一值的数量。
示例:每个客户订购的不同产品数量
使用多条件过滤分组
HAVING 支持任意布尔表达式,包括 AND/OR 组合。
示例:高价值的活跃客户
查找订单数大于 1 且总收入超过 1000 的客户:
实战示例:销售报表
把前面内容组合起来,构建一个完整的销售汇总报表。
示例:品类表现报表
示例:按收入排序的畅销产品
GROUP BY 与窗口函数
GROUP BY 会把多行折叠为每组一行。窗口函数则保留所有行,只是额外添加一个计算列 —— 详情见 SQL 窗口函数教程。
示例:GROUP BY 每个部门只返回一行
示例:窗口函数保留所有行
GROUP BY 的常见用例
- 报表仪表盘 —— 按类别或时间段汇总总数、平均值和计数
- 人力/数量分析 —— 按分组统计员工、用户或记录数量
- 收入拆分 —— 按产品、区域或客户统计总收入和平均收入
- 趋势分析 —— 按月份或年份分组,发现时间序列模式
- 每组 Top-N —— 结合子查询或 CTE 找出每个分组中的最佳表现者
- 数据校验 —— 按分组统计重复值或 NULL 值
- 分群分析(Cohort) —— 按注册日期或行为分群用户
性能优化建议
- 为 GROUP BY 列建立索引 —— 对分组列建立索引可以加速排序和分组步骤
- 用 WHERE 尽早过滤 —— 在分组前用 WHERE 减少行数,比事后用 HAVING 过滤更快
- 避免 SELECT * —— 只选择真正需要的列;聚合操作在数据更窄时性能更好
- 使用覆盖索引 —— 同时包含 GROUP BY 列和聚合列的索引可以避免全表扫描
- 考虑物化视图 —— 对于大表上频繁计算的聚合,一些数据库支持预计算汇总结果
小结
- GROUP BY 会把具有相同列值的多行折叠为每种唯一组合一行的汇总结果
- COUNT(*) 统计所有行;COUNT(column) 统计非 NULL 值;COUNT(DISTINCT column) 统计唯一值数量
- SUM 对数值求和;AVG 计算平均值;MIN / MAX 找出边界值
- SELECT 中的每一列要么出现在 GROUP BY 中,要么被聚合函数包裹
- WHERE 在分组前过滤行;HAVING 在聚合后过滤分组
- 你可以按表达式 GROUP BY(例如用
SUBSTR(date, 1, 7)做按月分组) - 先 JOIN 再 GROUP BY,可以把多数据源合并到一个报表中
- 当你需要在不丢失明细行的情况下获得分组级指标时,用窗口函数而不是 GROUP BY
试着把 GROUP BY 与 JOIN、CASE 表达式和 HAVING 子句结合起来,构建满足你数据分析需求的各种报表!