rollup按group by字段从左到右逐级上卷生成汇总行,等价于(a,b,c)、(a,b)、(a)、()四层分组union all,每层缺失字段以null占位;需用grouping()函数区分真实null与汇总行,字段顺序决定汇总层级。

ROLLUP生成的行是怎么来的
ROLLUP不是“额外加几行”,而是按列顺序从左到右逐级上卷,自动生成 GROUP BY 的所有前缀组合。比如 GROUP BY ROLLUP(a, b, c) 实际等价于四个分组的 UNION ALL:(a,b,c)、(a,b)、(a)、()。每层缺失的字段用 NULL 占位,这是语义约定,不代表原始数据为空。
常见错误现象:把 city IS NULL 当成数据缺失过滤掉,结果把城市小计和全国总计全删了。
- 字段顺序决定汇总路径——
ROLLUP(dept, team)生成部门小计;ROLLUP(team, dept)生成的是团队小计 - 层级越粗的字段越要放前面,比如想看“区域→城市→门店”,就得写
ROLLUP(region, city, store) - MySQL 8.0+ 用
GROUP BY ROLLUP(...);旧版 MySQL 和部分版本需写GROUP BY ... WITH ROLLUP
怎么区分小计行和真实NULL值
原始表里 region 字段本就允许为 NULL,仅靠 IS NULL 无法判断是明细里的空值,还是 ROLLUP 生成的“华东区小计”行。必须用 GROUPING() 函数。
GROUPING(col) 对当前行中被上卷的列返回 1,否则返回 0。它是跨数据库兼容的安全标识方式。
-
GROUPING(region) = 1 AND GROUPING(city) = 0→ 表示该行是“某城市下所有门店的汇总”,即城市小计 -
GROUPING(region) = 1 AND GROUPING(city) = 1→ 全表总计 - 别用
COALESCE(city, '小计')直接替换,会把真实city IS NULL的明细也标错
ROLLUP在不同数据库的写法差异
语法不统一是实操中最容易卡住的地方。PostgreSQL、SQL Server、Oracle 支持标准 GROUP BY ROLLUP(a,b);MySQL 8.0+ 也支持,但旧版只认 WITH ROLLUP 后缀,且不支持嵌套写法(如 ROLLUP((a,b), c))。
- SQL Server / PostgreSQL:
SELECT region, city, SUM(sales) FROM t GROUP BY ROLLUP(region, city) - MySQL(8.0+):
SELECT region, city, SUM(sales) FROM t GROUP BY region, city WITH ROLLUP - MySQL(UNION ALL 模拟
- PostgreSQL 若需 CUBE 行为,得用
GROUPING SETS显式列出所有组合,没有原生WITH CUBE
为什么ROLLUP结果看起来“重复又混乱”
根本原因是没控制排序和标注逻辑。ROLLUP 输出的行顺序默认由数据库引擎决定,NULL 占位行可能插在明细中间;而 ORDER BY 若直接写 ORDER BY region, city,会把“华东区小计”(city IS NULL)排到“华东-上海”之后、“华东-南京”之前,视觉上断裂。
- 推荐用
GROUPING_ID(region, city)或组合GROUPING()值排序,让明细→小计→总计严格分层 - 聚合字段必须全部出现在
SELECT列表里,且每个非聚合列都要参与GROUP BY ROLLUP,否则报错column "xxx" must appear in the GROUP BY clause - 高基数字段(如
user_id)上用 ROLLUP,会生成海量小计行,结果集膨胀严重,报表加载慢甚至超时
GROUPING() 标注层级——不是加不加的问题,是加错位置、加错条件,就会让小计和明细混在一起,下游系统解析出错。











