rollup按group by列从左到右逐级上卷生成层级,如group by a,b,c产生(a,b,c)、(a,b,null)、(a,null,null)、(null,null,null)四层,null代表该维度被折叠,须用grouping()区分汇总null与真实null。

ROLLUP 生成的分组层级是怎么算的?
ROLLUP 不是简单叠加小计,它按字段顺序生成「从右到左」的递归聚合层级。比如 GROUP BY ROLLUP(a, b, c) 实际等价于:GROUP BY a, b, c(明细)、GROUP BY a, b(c 维度小计)、GROUP BY a(b+c 维度小计)、GROUP BY ()(全表总计)。顺序错了,小计粒度就完全不对。
常见错误是把高粒度字段放前面、低粒度放后面,结果小计变成“按年汇总后再按月拆分”,逻辑反了。正确做法是按分析维度从粗到细排列:比如先 region,再 department,最后 employee_id。
NULL 值在 ROLLUP 结果里代表什么?
ROLLUP 用 NULL 标识聚合行,但这个 NULL 不是数据缺失,而是“该维度被折叠”。比如 SELECT region, department, SUM(salary) FROM t GROUP BY ROLLUP(region, department) 中,region = 'East' 且 department IS NULL 行,表示“East 地区所有部门合计”;而 region IS NULL 且 department IS NULL 行,才是全表总计。
容易踩的坑:
- 直接
WHERE department IS NULL会把真正的空部门数据和 ROLLUP 小计混在一起 - 没用
GROUPING()函数区分真实NULL和 ROLLUPNULL,导致报表显示“部门:NULL”让人困惑 - MySQL 8.0+ 支持
GROUPING(region)返回 1 表示该字段被聚合,可配合CASE替换显示为“总计”或“小计”
ROLLUP 和 CUBE、GROUPING SETS 有什么关键区别?
ROLLUP(a,b,c) 只生成 (a,b,c) → (a,b) → (a) → () 四层,是单向嵌套;CUBE(a,b,c) 生成所有组合(包括 (b,c)、(a,c) 等),结果行数指数级增长;GROUPING SETS 则让你手动指定要哪几组,比如 GROUPING SETS ((a,b), (a), ()) 只要部门级小计和总计,跳过 (b) 单独聚合。
选型建议:
- 做标准的“逐级上卷”报表(如:城市→省份→全国),用
ROLLUP - 需要交叉分析(如:既要按产品又要按时间,还要两者组合),考虑
CUBE,但注意性能爆炸风险 - 只要特定几组聚合(比如只看各区域总和 + 全公司总和,不看各部门),
GROUPING SETS更精准、更省资源
不同数据库对 ROLLUP 的兼容性要注意什么?
PostgreSQL 和 SQL Server 完全支持标准 ROLLUP 语法;MySQL 5.7 开始支持,但 5.7 默认关闭 ONLY_FULL_GROUP_BY 模式时可能允许非确定性字段出现在 SELECT 中,导致结果不稳定;SQLite 目前不支持 ROLLUP,得用 UNION ALL 拼接多条 GROUP BY 查询模拟。
实操提醒:
- Oracle 用户注意:旧版用
ROLLUP关键字,新版也支持GROUPING SETS,但语法细节略有差异 - 如果查询里用了窗口函数,ROLLUP 聚合和窗口计算不能共存于同一层 SELECT,必须子查询或 CTE 分离
- 聚合字段较多时(>5 个),ROLLUP 结果行数 ≈ 2ⁿ,务必加 WHERE 过滤再聚合,别让数据库先全量分组再裁剪
真正麻烦的不是写法,而是搞清哪些维度该进 ROLLUP、哪些该留在 WHERE 或 HAVING 里过滤——一不留神,小计就变成了“含异常数据的假合计”。











