在编写报表类SQL时,我们往往希望一条查询既能输出明细分组,又能顺带算出小计和总计。ROLLUP就是GROUP BY的一个扩展子句,它可以按照分组列从左到右依次减少维度,自动生成更高层级的聚合行,从而用最简洁的语句完成小计汇总。

ROLLUP基本语法
ROLLUP通常跟在GROUP BY之后,语法形式如下:
SELECT 列1, 列2, 聚合函数(列3) FROM 表名 GROUP BY ROLLUP (列1, 列2);
以上写法表示先按列1和列2细分,再按列1做小计,最后生成全部数据的总计。在结果集中,被省略的维度会以NULL显示。
一个完整示例
假设有销售表sales,包含地区region、年份yr和金额amount,我们想看各地区每年的销售额、各地区小计以及全局汇总:
SELECT region, yr, SUM(amount) AS total_amount FROM sales GROUP BY ROLLUP (region, yr) ORDER BY region, yr;
执行后可能出现这样的结果:
| region | yr | total_amount |
|---|---|---|
| 华东 | 2021 | 1000 |
| 华东 | 2022 | 1200 |
| 华东 | NULL | 2200 |
| 华北 | 2021 | 800 |
| 华北 | NULL | 800 |
| NULL | NULL | 3000 |
其中region为NULL且yr为NULL的行就是整体总计,region非NULL但yr为NULL的是对应地区的小计。
用GROUPING区分汇总行
NULL在原始数据里也可能出现,为了明确分辨是ROLLUP生成的汇总行,可以使用GROUPING函数。它返回1表示该行是该列的汇总,返回0表示普通分组:
SELECT region, yr, SUM(amount) AS total_amount, GROUPING(region) AS grp_region, GROUPING(yr) AS grp_yr FROM sales GROUP BY ROLLUP (region, yr);
给汇总行加标签
结合CASE和GROUPING,能把NULL改成易读的文字:
SELECT CASE WHEN GROUPING(region)=1 THEN '全部地区' ELSE region END AS region, CASE WHEN GROUPING(yr)=1 THEN '全部年份' ELSE CAST(yr AS CHAR) END AS yr, SUM(amount) AS total_amount FROM sales GROUP BY ROLLUP (region, yr);
使用注意点
- ROLLUP括号内列的顺序决定小计层级,调换顺序会得到不同的汇总路径。
- 在MySQL中可直接使用上述语法;PostgreSQL同样支持标准ROLLUP。
- 如果只需要部分维度的小计,可配合GROUPING SETS更灵活控制。
ROLLUP是一种轻量的分析查询能力,掌握后能用一条SQL替代多层UNION ALL,既提升可读性也减少数据库计算开销。