在PostgreSQL的数据分析场景中,多维聚合是高频需求,比如同时统计不同部门、不同年份、不同产品的销售额总和,传统写法需要多次执行GROUP BY再联合结果,而GROUPING SETS可以直接在一个查询中完成所有维度组合的聚合,大幅简化逻辑。

GROUPING SETS基础概念
GROUPING SETS是PostgreSQL支持的SQL高级聚合功能,属于GROUP BY的扩展语法,允许在一个查询中指定多组分组维度,数据库会分别计算每组维度的聚合结果,最终将所有结果合并返回。它的作用等价于多个不同GROUP BY语句的结果用UNION ALL拼接,但执行效率更高,因为只需要扫描一次数据表。
GROUPING SETS的语法格式如下:
SELECT
分组列1,
分组列2,
聚合函数(计算列) AS 聚合结果,
GROUPING(分组列1, 分组列2) AS 分组标识
FROM 表名
GROUP BY GROUPING SETS (
(分组列1, 分组列2),
(分组列1),
(分组列2),
()
)
其中GROUPING函数用于标识当前结果行对应的分组维度,返回值是整数,对应分组列是否被聚合:如果分组列在当前的GROUPING SET中被使用,返回0,否则返回1。比如GROUPING(部门, 年份)的结果中,若当前行是按部门和年份分组,返回0;若只按部门分组,年份被聚合,返回1;若两者都被聚合,返回3。
基础使用示例
假设我们有销售数据表sales,结构如下:
CREATE TABLE sales (
id INT PRIMARY KEY,
department VARCHAR(50), -- 部门
sale_year INT, -- 销售年份
product VARCHAR(50), -- 产品
amount DECIMAL(10,2) -- 销售额
);
-- 插入测试数据
INSERT INTO sales VALUES
(1, '技术部', 2023, '笔记本', 12000),
(2, '技术部', 2023, '键盘', 800),
(3, '技术部', 2024, '笔记本', 15000),
(4, '销售部', 2023, '键盘', 1200),
(5, '销售部', 2024, '鼠标', 600),
(6, '销售部', 2024, '键盘', 1500);
现在需要同时统计以下维度的销售额总和:
- 部门+年份组合的总销售额
- 仅部门维度的总销售额
- 仅年份维度的总销售额
- 所有数据的总销售额
使用GROUPING SETS实现如下:
SELECT
department,
sale_year,
SUM(amount) AS total_amount,
GROUPING(department, sale_year) AS group_flag
FROM sales
GROUP BY GROUPING SETS (
(department, sale_year),
(department),
(sale_year),
()
)
ORDER BY department, sale_year;
执行结果如下:
| department | sale_year | total_amount | group_flag |
|---|---|---|---|
| 技术部 | 2023 | 12800 | 0 |
| 技术部 | 2024 | 15000 | 0 |
| 技术部 | null | 27800 | 1 |
| 销售部 | 2023 | 1200 | 0 |
| 销售部 | 2024 | 2100 | 0 |
| 销售部 | null | 3300 | 1 |
| null | 2023 | 14000 | 2 |
| null | 2024 | 17100 | 2 |
| null | null | 31100 | 3 | >
可以看到,结果中包含了所有指定的分组维度组合,group_flag字段清晰标识了每行的分组逻辑,null值代表该维度被聚合,不参与分组。
与传统实现方式对比
如果使用传统方式实现上述需求,需要分别编写4个GROUP BY查询再用UNION ALL合并,代码如下:
-- 部门+年份分组 SELECT department, sale_year, SUM(amount) AS total_amount, 0 AS group_flag FROM sales GROUP BY department, sale_year UNION ALL -- 仅部门分组 SELECT department, NULL AS sale_year, SUM(amount) AS total_amount, 1 AS group_flag FROM sales GROUP BY department UNION ALL -- 仅年份分组 SELECT NULL AS department, sale_year, SUM(amount) AS total_amount, 2 AS group_flag FROM sales GROUP BY sale_year UNION ALL -- 全量聚合 SELECT NULL AS department, NULL AS sale_year, SUM(amount) AS total_amount, 3 AS group_flag FROM sales ORDER BY department, sale_year;
对比两种方式,GROUPING SETS的优势非常明显:
- 代码更简洁,不需要重复编写多个查询和UNION ALL逻辑
- 执行效率更高,只需要扫描一次数据表,传统方式需要扫描四次
- 结果更规范,GROUPING函数自动生成分组标识,不需要手动定义
实际应用场景
多维度报表统计
在生成业务报表时,经常需要同时展示不同粒度的统计数据,比如电商平台的销售报表,需要同时按日、按月、按季度、按商品类目统计销售额,使用GROUPING SETS可以一次性生成所有维度的数据,减少后端查询次数。
数据分层汇总
当需要生成包含小计、总计的分层汇总数据时,GROUPING SETS可以快速实现。比如统计各部门各产品的销售额,同时生成部门小计和全公司总计,只需要在GROUPING SETS中指定对应的分组组合即可。
动态维度分析
如果业务需要支持用户自主选择分组维度,比如用户可以选择按部门、年份、产品中的任意组合分组,后端可以动态拼接GROUPING SETS的参数,不需要编写大量的条件判断逻辑。
注意事项
使用GROUPING SETS时需要注意以下几点:
- GROUPING SETS中的空括号
()代表全量聚合,即不按任何维度分组,计算所有数据的总聚合值 - 如果分组列中存在null值,会和GROUPING SETS生成的null聚合标识混淆,这种情况下可以使用GROUPING函数区分,或者使用COALESCE函数将null替换为特殊值
- GROUPING SETS可以和CUBE、ROLLUP一起使用,CUBE会生成所有可能的维度组合,ROLLUP会生成层级维度的组合,根据需求选择合适的语法即可
GROUPING SETS是PostgreSQL多维聚合的核心功能,熟练掌握后可以大幅提升复杂数据分析查询的编写效率,减少不必要的重复代码和执行开销。
PostgreSQLGROUPING_SETS多维聚合SQL查询修改时间:2026-06-30 14:12:39