WITH ROLLUP 用 NULL 标记折叠层级而非简单置空,如 region 非空而 city 为 NULL 表示该 region 小计,双 NULL 才是总计;需用 GROUPING() 或组合判空区分真假 NULL,避免 ORDER BY 破坏层级顺序。

WITH ROLLUP 会自动加 NULL 行,但不是所有 NULL 都代表汇总
MySQL 的 WITH ROLLUP 确实会在分组结果末尾插入汇总行,但它用 NULL 标记“这一层被折叠了”,而不是简单地把所有字段设为空。比如按 region 和 city 分组时,city 为 NULL 且 region 不为 NULL 的行,才是该 region 的小计;而 region 和 city 同时为 NULL 的行,才是总计。
常见错误是直接 WHERE city IS NULL 想过滤出小计——这会把总计也捞进来,还可能误伤真实数据里本来就为 NULL 的城市名。
- 用
GROUPING()函数(MySQL 8.0.12+)判断是否由 ROLLUP 生成:例如GROUPING(city) = 1表示此行的 city 是汇总占位符 - 低版本 MySQL 只能靠组合判空 + 字段顺序推断:先确认分组字段顺序,再用
city IS NULL AND region IS NOT NULL定位小计 - 注意:如果原始数据中
city允许为NULL,必须提前处理(如转成'(unknown)'),否则无法区分真假 NULL
ORDER BY 和 WITH ROLLUP 的顺序冲突很隐蔽
WITH ROLLUP 依赖 GROUP BY 的字段顺序生成层级,但如果你在后面加 ORDER BY,MySQL 会先排序再加汇总行——导致汇总行跑到中间,甚至被分页截断。
典型现象:执行 SELECT region, city, SUM(sales) FROM t GROUP BY region, city WITH ROLLUP ORDER BY region, sales DESC,结果里某个 region 的小计出现在该 region 的某条明细之后,完全打乱层级。
- 正确做法是去掉
ORDER BY,靠 GROUP BY 顺序控制汇总位置;如真需排序,改用窗口函数或子查询包装 - 如果必须前端排序,至少保留
region和city在 ORDER BY 最前,避免跨 region 混排 - MySQL 5.7 及更早版本不支持在含
WITH ROLLUP的语句中用ORDER BY引用别名,会报错Unknown column 'xxx' in 'order clause'
聚合函数遇到 ROLLUP 行时不会报错,但结果可能不符合直觉
SUM()、COUNT() 这些函数对 ROLLUP 行照常计算,但 COUNT(*) 在汇总行里统计的是该层级的明细行数,不是“所有下属行之和”——这点容易误解。
比如按 region、city 分组,某 region 下有 3 个城市,每个城市各 2 条订单,则该 region 小计行的 COUNT(*) 是 3(不是 6),因为它是对 city 维度做聚合后产生的新行。
- 需要“该 region 总订单数”,得用
SUM(order_count)(假设你已用子查询算好每 city 的数量)或改用SUM(COUNT(*)) OVER (PARTITION BY region)(8.0+) -
AVG()在 ROLLUP 行里是对本层非 NULL 值平均,不是加权平均,慎用于业务指标 - 避免在 SELECT 列表里混用带条件的聚合(如
COUNT(IF(status='done',1,NULL)))和 ROLLUP,逻辑更难追踪
替代方案:ROLLUP 不是唯一选择,尤其跨数据库时
PostgreSQL 没有 WITH ROLLUP,SQL Server 用 GROUPING SETS,SQLite 不支持分组汇总语法。硬写 ROLLUP 会让 SQL 失去移植性。
更可控的做法是用 UNION ALL 显式拼接明细、小计、总计三层结果,虽然多几行 SQL,但逻辑清晰、各层独立可调、兼容性强。
- 小计层用
GROUP BY region,总计层用GROUP BY ()或直接SELECT SUM(...) FROM t - 每层 SELECT 加一个
level字段(如'detail'/'region_total'/'grand_total'),方便应用层识别 - 性能上,现代 MySQL 对 ROLLUP 优化较好,但 UNION 方案更容易命中索引,尤其当某些分组层级数据极少时
真正麻烦的从来不是语法怎么写,而是业务方看到一堆 NULL 行时问“这个 NULL 是没填还是合计”,得提前约定好展示层怎么翻译这些标记。

















