rollup 是 group by 的修饰符,必须写成 group by rollup(...);按列从左到右生成分层小计,汇总行中 null 表示“全部”而非缺失,需用 grouping() 函数区分,不支持的数据库需用 union all 等替代,性能开销显著高于普通 group by。

ROLLUP 是 GROUP BY 的扩展,不是独立语句
ROLLUP 不是单独使用的语法,它必须作为 GROUP BY 的修饰符出现。写成 GROUP BY ROLLUP(...) 才合法;如果漏掉 GROUP BY 直接写 ROLLUP,会报错 ERROR: syntax error at or near "ROLLUP"。
它按括号内列的**从左到右顺序**生成分层小计:先按最左列分组,再加入第二列形成更细粒度分组,最后加一个全表总计行(该行对应列值为 NULL)。
-
GROUP BY ROLLUP(a, b)产生三组结果:(a,b)、(a, NULL)、(NULL, NULL) -
GROUP BY ROLLUP(a, b, c)产生四组:(a,b,c)、(a,b,NULL)、(a,NULL,NULL)、(NULL,NULL,NULL) - 列顺序不能随意调换——
ROLLUP(b, a)小计逻辑完全不同
NULL 值在 ROLLUP 结果中代表“全部”而非缺失数据
ROLLUP 自动生成的汇总行里,被“折叠”的列显示为 NULL。这不是数据缺失,而是 SQL 标准定义的占位符,表示“这一级不区分”。直接用 WHERE col IS NULL 过滤会误删真实 NULL 数据,也抓不到所有汇总行。
正确做法是用 GROUPING() 函数识别汇总层级:
SELECT COALESCE(dept, '总计') AS dept, COALESCE(team, '小计') AS team, SUM(salary) AS total FROM staff GROUP BY ROLLUP(dept, team) HAVING GROUPING(dept) = 0; -- 只保留 dept 级别及以上分组(排除纯总计行)
-
GROUPING(dept)返回 1 表示该行 dept 是 ROLLUP 生成的汇总值(即NULL占位),0 表示真实数据 -
COALESCE()仅用于展示美化,不影响分组逻辑 - 避免在
SELECT中对 ROLLUP 列做IS NULL判断来分类,容易混淆真实 NULL 和汇总 NULL
ROLLUP 在不同数据库中的兼容性差异
PostgreSQL、SQL Server、Oracle 原生支持 ROLLUP;MySQL 8.0+ 支持,但 5.7 及更早版本不支持,强行使用会报错 Unknown column 'ROLLUP' in 'group statement'。
- MySQL 5.7 替代方案:用
UNION ALL手动拼接明细、小计、总计查询(性能差、易出错) - SQLite 不支持 ROLLUP,需用
GROUPING SETS(若版本 ≥ 3.25.0)或彻底改写逻辑 - 某些旧版 PostgreSQL 需启用
standard_conforming_strings = on才能解析 ROLLUP 语法
性能开销比普通 GROUP BY 明显更高
ROLLUP 实际执行时,数据库要多跑几次聚合扫描——不是一次扫描出所有层级,而是模拟多个 GROUP BY 子查询再合并。数据量大时,GROUP BY ROLLUP(a,b,c) 可能比 GROUP BY a,b,c 慢 2–5 倍。
- 避免在高频查询或大宽表上无节制使用多列 ROLLUP
- 如只需某一层小计(比如只要部门小计,不要团队小计),改用
GROUP BY dept+ 单独查总计更高效 - 确认执行计划中是否出现
GroupAggregate或HashAggregate多次,这是 ROLLUP 开销的典型特征
ROLLUP 的层级逻辑和 NULL 含义是硬性规则,没法绕过;写错顺序或忽略 GROUPING() 就容易把小计当脏数据过滤掉。










