ROLLUP是GROUP BY的扩展语法,用于自动生成多级小计和总计行;它必须与GROUP BY联用,按列从左到右逐级上卷生成分组:如GROUP BY ROLLUP(a,b,c)产生(a,b,c)、(a,b,NULL)、(a,NULL,NULL)、(NULL,NULL,NULL)四组结果。

ROLLUP 是什么,它和 GROUP BY 有什么区别
ROLLUP 是 GROUP BY 的扩展语法,用于在分组聚合结果中自动添加多级小计和总计行。它不是独立语句,必须配合 GROUP BY 使用,本质是按指定列顺序生成“层级递减”的分组组合:从最细粒度(所有列)→ 逐列向上省略 → 最终空集(全表总计)。
常见误解是把它当“开关”用,其实它的行为严格依赖列顺序。比如 GROUP BY ROLLUP(a, b, c) 会生成 4 组结果:(a,b,c)、(a,b)、(a)、()(空括号代表全表总计)。如果顺序写反了,小计逻辑就完全错位。
注意:ROLLUP 在 MySQL 8.0+、PostgreSQL 9.5+、SQL Server、Oracle 中原生支持;但 SQLite 和旧版 MySQL(ERROR 1064 或提示语法错误。
怎么写一个带小计和总计的销售汇总 SQL
核心是把维度列按“从高到低的汇总层级”排列,再用 ROLLUP 包裹。例如按地区 → 城市 → 销售员统计销售额:
SELECT COALESCE(region, '总计') AS region, COALESCE(city, CASE WHEN region IS NULL THEN '' ELSE '小计' END) AS city, COALESCE(salesperson, CASE WHEN city IS NULL THEN '' ELSE '小计' END) AS salesperson, SUM(amount) AS total_amount FROM sales GROUP BY ROLLUP(region, city, salesperson);
关键点:
-
COALESCE用来识别NULL行(ROLLUP用NULL标识上层小计/总计) - 地区小计行:city 和 salesperson 为
NULL,region 有值 - 城市小计行:salesperson 为
NULL,region 和 city 有值 - 总计行:region、city、salesperson 全为
NULL - 不要漏掉
ORDER BY,否则小计行可能散落在中间,建议加ORDER BY region, city, salesperson
为什么有时候小计值对不上,或者多出奇怪的 NULL 行
这是最常踩的坑:把非维度列(比如时间字段、状态字段)混进 ROLLUP 列表,或没处理好 NULL 含义。
常见错误现象:
- 小计行的金额比预期大 —— 可能漏了
WHERE过滤条件,ROLLUP会在所有满足 WHERE 的数据上计算,包括被逻辑排除的记录 - 出现大量重复小计 —— 把
id或唯一键放进ROLLUP,导致每行都成一个“最小分组”,失去汇总意义 - 小计行显示
NULL却没替换文字 —— 没用COALESCE或CASE处理,直接展示数据库层面的NULL,易被误读为缺失数据
正确做法:
-
ROLLUP列表只放真正用于分析的维度列(如部门、产品类别、月份) - 所有非分组字段必须出现在聚合函数中(
SUM()、COUNT()等),不能裸写 - 如果某列允许
NULL值(比如可选的城市字段),先用WHERE city IS NOT NULL过滤,否则ROLLUP会把业务NULL和汇总NULL混在一起,无法区分
ROLLUP 和 CUBE、GROUPING SETS 性能与可读性对比
ROLLUP(a,b,c) 生成 4 组分组;CUBE(a,b,c) 生成 8 组(所有排列组合);GROUPING SETS 则完全手动指定,比如 GROUPING SETS ((a,b), (a), ())。
实际选型看需求:
- 只需要“逐级向上汇总”(如部门→公司→全部),用
ROLLUP最简洁,执行计划也更可控 - 需要交叉小计(比如既要按部门,也要按产品线),
CUBE易写但结果爆炸,10 个维度就是 2¹⁰=1024 组,慎用 - 要精确控制哪些组合出现(比如只要部门小计和全表总计,不要城市级),必须用
GROUPING SETS,但可读性差,维护成本高
另外,ROLLUP 在大数据量下仍走索引范围扫描(如果分组列有合适索引),但 CUBE 很可能触发全表扫描 + 内存哈希,观察执行计划时重点看 Extra 列是否出现 Using temporary; Using filesort。
GROUPING() 函数可以辅助判断当前行是否为小计/总计(返回 1 表示该列为 ROLLUP 生成的 NULL),比层层 IS NULL 判断更可靠,但不是所有数据库都支持(MySQL 8.0+ 支持,PostgreSQL 用 GROUPING(),SQL Server 也支持)。
实际用的时候,别只盯着语法写对了,先想清楚“哪些维度要小计”“小计之间有没有逻辑包含关系”,再决定列顺序——顺序错了,整个汇总结构就塌了。

















