ROLLUP按字段顺序构建树状层级,如ROLLUP(a,b,c)生成(a,b,c)、(a,b)、(a)、()四层,从细到粗逐级退化,NULL表示“该维不区分”而非缺失,须用GROUPING()精准识别汇总行。

ROLLUP 生成的分组层级怎么理解?
ROLLUP 不是简单叠加 GROUP BY,而是按字段顺序构建树状分组层级。比如 GROUP BY ROLLUP(a, b, c) 实际生成 4 组结果:(a,b,c)、(a,b)、(a)、() —— 最后一组就是全表总计。关键点在于顺序决定层级深度,ROLLUP(a, b) 和 ROLLUP(b, a) 的小计逻辑完全不同。
常见错误是把 ROLLUP 当作“自动加合计行”,结果发现小计位置不对或漏掉某层汇总。本质是它按左到右逐级退化:保留最左字段 → 去掉最右字段 → 再去掉一个 → 直至空组。
如何识别小计行和总计行?
ROLLUP 产生的空值不是数据缺失,而是标记符。MySQL 和 PostgreSQL 中,小计/总计行对应字段为 NULL;SQL Server 还支持 GROUPING() 函数返回 1/0 显式判断。但更通用、兼容性更好的方式是用 GROUPING_ID() 或组合 GROUPING() 判断:
SELECT COALESCE(region, '【总计】') AS region, COALESCE(dept, '【小计】') AS dept, SUM(sales) AS total FROM sales GROUP BY ROLLUP(region, dept);
注意:COALESCE 只是显示替换,不能区分 (region=NULL, dept=xxx) 是小计还是原始 NULL 数据 —— 真实业务中必须结合 GROUPING() 或明确字段非空约束。
ROLLUP 和 CUBE、GROUPING SETS 有什么实际区别?
三者都扩展 GROUP BY,但行为差异直接影响结果集大小和语义:
-
ROLLUP(a,b,c):只生成前缀组合,共 n+1 组(n 是字段数),适合层级报表(如省→市→区) -
CUBE(a,b,c):生成全部排列组合,2ⁿ 组,易爆炸,仅当真需交叉汇总时用 -
GROUPING SETS((a),(b),(a,b),()):完全手动指定,灵活但写法冗长,适合混合粒度需求
误用 CUBE 替代 ROLLUP 是常见性能坑:10 个字段的 CUBE 会产生 1024 行分组,而 ROLLUP 只有 11 行。
ORDER BY 和 ROLLUP 结果排序不一致怎么办?
ROLLUP 生成的分组顺序由数据库实现决定,不保证与 ORDER BY 一致。尤其在 PostgreSQL 中,ORDER BY region, dept 可能导致小计行被穿插在明细中间。
可靠做法是用 GROUPING() 控制排序优先级:
SELECT region, dept, SUM(sales) FROM sales GROUP BY ROLLUP(region, dept) ORDER BY GROUPING(region), region, GROUPING(dept), dept;
这里 GROUPING(region) 对总计行返回 1,对其他行返回 0,确保总计排最后;同理控制 dept 小计位置。忽略这点,报表导出后合计数错位是高频线上问题。
真正麻烦的是嵌套 ROLLUP 或配合窗口函数时,GROUPING 值含义会变复杂 —— 这时候不如拆成 UNION ALL 多个独立查询,反而更可控、易调试。

















