ROLLUP生成层级递减小计,CUBE穷举所有分组组合;二者均用NULL标记汇总行,必须配合GROUPING()或GROUPING_ID()识别,不可直接WHERE过滤,且性能随维度增加急剧下降。

ROLLUP 和 CUBE 是 Oracle 中直接嵌入 GROUP BY 的汇总扩展语法,不是函数,也不需要额外调用。它们能自动生成小计、合计甚至多维组合统计行,但行为差异大、NULL 含义易混淆,必须配合 GROUPING() 或 GROUPING_ID() 才能安全识别汇总行。
ROLLUP 生成的是层级递减的小计,顺序决定汇总逻辑
比如 GROUP BY ROLLUP(deptno, job) 实际执行四层分组:(deptno, job) → (deptno) → 全表总计。它从右往左“剥洋葱”,右边字段最先被忽略。
- 若写成
ROLLUP(job, deptno),则先按(job, deptno)分,再按job小计,最后全表合计 —— 小计维度完全不同 - 结果中出现的
NULL不是原始数据缺失,而是 Oracle 标记该列为“汇总占位”,例如deptno IS NULL AND job IS NOT NULL这种组合在原始数据里根本不可能存在 - 不加处理直接
ORDER BY deptno, job可能把汇总行插在中间,建议用GROUPING_ID(deptno, job)控制排序:值越小越底层(如 0=明细,1=deptno 小计,3=总计)
CUBE 会穷举所有列组合,结果行数爆炸需警惕
GROUP BY CUBE(a, b, c) 等价于对全部 2³ = 8 种非空子集做 GROUP BY:即 (a,b,c)、(a,b)、(a,c)、(b,c)、(a)、(b)、(c)、()(全表)。比 ROLLUP 多出大量交叉小计。
- 3 列时输出最多 8 倍明细行数;4 列就是 16 倍 —— 数据量稍大就明显拖慢查询,别盲目套用
-
CUBE生成的NULL更难直觉判断归属,比如a=NULL, b=10, c=NULL表示只按b分组的统计,必须靠GROUPING(a)+GROUPING(c)联合判断 - 如果只要某几个特定组合(如既要按部门小计,又要按职位小计,但不要部门+职位交叉),
CUBE是杀鸡用牛刀,改用UNION ALL拼接多个GROUP BY更可控
必须用 GROUPING() 区分真实 NULL 和汇总占位 NULL
原始数据里的 NULL 和 ROLLUP/CUBE 自动生成的占位 NULL 在结果里完全无法肉眼分辨。直接 WHERE deptno IS NULL 会误删真实数据,也会漏掉本该保留的汇总行。
-
GROUPING(deptno)返回 1 表示这行的deptno是汇总占位(即该字段未参与本次分组),返回 0 表示来自实际数据 - 常用写法:
CASE WHEN GROUPING(deptno) = 1 THEN '总计' ELSE TO_CHAR(deptno) END AS dept_label - 多列时推荐
GROUPING_ID(deptno, job):它把各列的GROUPING()结果当二进制位拼起来转十进制,比如(0,1)→ 十进制 1,(1,1)→ 十进制 3,排序和条件过滤都更紧凑
ROLLUP 和 CUBE 都不能带 WHERE 条件过滤汇总行
HAVING 只作用于分组后的聚合结果,但它无法区分哪些是明细组、哪些是小计组。想筛掉某类小计(比如不要部门级小计,只要职位级和总计),HAVING 无能为力。
- 错误写法:
HAVING GROUPING(deptno) = 0——HAVING发生在分组计算之后,但GROUPING()是伪列,Oracle 不允许在HAVING中引用它 - 正确做法:外层套一层查询,用
WHERE过滤GROUPING_ID(),例如SELECT * FROM (SELECT ..., GROUPING_ID(a,b) gid FROM t GROUP BY ROLLUP(a,b)) WHERE gid IN (0, 3) - 性能注意:外层
WHERE无法下推,汇总计算仍会全量执行,大数据量时慎用
真正麻烦的不是写法,而是理解每一行的语义来源 —— ROLLUP 的层级性、CUBE 的组合爆炸、以及那些看起来一样实则含义天差地别的 NULL。不加 GROUPING() 直接上线,报表数字对不上是大概率事件。


















