rollup 是 group by 的扩展子句,用于生成多级小计和总计行,仅按括号内列从左到右的前缀子集分组(如 rollup(a,b,c) 生成 (a,b,c)、(a,b)、(a)、()),不支持任意组合;需用 cube 或 union all 实现其他维度汇总。

ROLLUP 是什么,它和 GROUP BY 有什么区别
ROLLUP 不是独立语句,而是 GROUP BY 的扩展子句,用于在分组结果中自动添加多级小计和总计行。它按括号内列的从左到右顺序逐层上卷:先按全部列分组,再少最右一列,再少两列……直到只剩第一列,最后加一行全表总计。
常见错误是以为 ROLLUP(a, b, c) 会生成所有排列组合的小计(比如只按 b 或只按 c),实际不会——它只生成前缀子集:(a,b,c)、(a,b)、(a)、()。如果需要其他维度小计,得换用 CUBE 或手动 UNION ALL。
使用场景集中在报表汇总:销售按地区+部门+月份统计时,既要各月明细,也要各部门月度小计、各地区季度小计、全公司总计。
怎么写一个带 ROLLUP 的基础查询
核心写法就是把 ROLLUP 放在 GROUP BY 后面,括号里填分组字段:
SELECT COALESCE(region, '总计') AS region, COALESCE(dept, '小计') AS dept, SUM(sales) AS total_sales FROM sales_data GROUP BY region, dept WITH ROLLUP;
注意三点:
-
WITH ROLLUP是 MySQL 语法;PostgreSQL 和 SQL Server 用GROUP BY ROLLUP(region, dept)(无WITH) -
NULL值代表该层级“未分组”,需用COALESCE或CASE替换为可读标签,否则容易误判为真实数据缺失 - 字段顺序影响层级结构:
GROUP BY ROLLUP(dept, region)会先出部门小计,再出全表总计,不按地理逻辑——顺序错了,小计归属就乱
ROLLUP 导致 NULL 值混淆怎么办
ROLLUP 生成的汇总行会在对应列填 NULL,但原始数据里也可能真有 NULL。单靠 IS NULL 判断不可靠。
推荐用 GROUPING() 函数(MySQL 8.0.12+、PostgreSQL、SQL Server 都支持):
SELECT CASE WHEN GROUPING(region) = 1 THEN '总计' ELSE region END AS region, CASE WHEN GROUPING(dept) = 1 THEN '小计' ELSE dept END AS dept, SUM(sales) FROM sales_data GROUP BY region, dept WITH ROLLUP;
GROUPING(region) 返回 1 表示这行的 region 是 ROLLUP 自动生成的汇总占位,不是原始 NULL。比 COALESCE 更精准,尤其当业务本身允许 region 为 NULL 时。
性能和兼容性要注意哪些坑
ROLLUP 本质是多次分组聚合后合并,数据量大时比普通 GROUP BY 多几轮计算,但一般不至于慢几个数量级——真正拖慢的是没建好索引。
- 确保
GROUP BY列(如region, dept)上有联合索引,顺序必须和ROLLUP中一致 - SQLite 不支持
ROLLUP,得用UNION ALL模拟,代码冗长且难维护 - Oracle 旧版本(ROLLUP 要加
GROUP BY子句外的括号,新版本已统一为GROUP BY ROLLUP(...)
小计行本身不参与后续 HAVING 过滤(因为聚合已完成),但 WHERE 仍生效——这点常被忽略:如果想排除某类原始记录,必须在 WHERE 阶段过滤,不能指望 HAVING 把小计也筛掉。
ROLLUP 的层级是静态定义的,没法运行时切换;真要灵活切维度,得结合应用层拼 SQL 或改用 OLAP 引擎。










