ROLLUP按GROUP BY列从左到右逐级收缩生成层级:(a,b,c)→(a,b)→(a)→(),顺序颠倒则汇总逻辑错误;NULL为聚合占位符,须用GROUPING()函数识别,不可用IS NULL过滤。

ROLLUP 生成的分组层级是怎么排列的
ROLLUP 不是简单叠加小计,它按 GROUP BY 列表从左到右逐级收缩:先全字段分组,再依次去掉最右边的字段,最后保留空分组(即总计行)。比如 GROUP BY ROLLUP(a, b, c) 实际产生四层:(a,b,c) → (a,b) → (a) → ()。顺序反了结果就错——ROLLUP(b, a, c) 的小计逻辑完全不同,别凭感觉调换字段顺序。
NULL 值在 ROLLUP 结果中代表什么
ROLLUP 用 NULL 标记被“折叠”的维度,但它不是真实数据缺失,而是聚合占位符。常见误解是直接过滤掉 NULL 行,结果把小计和总计全删了。正确做法是用 GROUPING() 函数识别:比如 GROUPING(city) = 1 表示该行 city 是 ROLLUP 生成的汇总行,而非原始数据为 NULL。多个字段时可套用 GROUPING(city) + GROUPING(region) 判断汇总层级。
ROLLUP 和 GROUPING SETS 的性能与可读性差异
ROLLUP 是语法糖,底层仍走多维分组,大数据量下和手写 UNION ALL 多个 GROUP BY 查询性能接近,但写法简洁。不过一旦需要跳过某层(比如只要 (a,b) 和 (),不要 (a)),ROLLUP 就无能为力,必须改用 GROUPING SETS。另外,MySQL 8.0+ 才支持 ROLLUP,5.7 及更早版本会报错 ERROR 1235 (42000): This version of MySQL doesn't yet support 'ROLLUP',别在旧环境硬试。
怎么让 ROLLUP 输出带明确标签的小计行
默认输出的 NULL 不友好,可用 CASE WHEN GROUPING(col) = 1 THEN '小计' ELSE col END 替换。例如:
SELECT CASE WHEN GROUPING(dept) = 1 THEN '总计' ELSE dept END AS dept, CASE WHEN GROUPING(team) = 1 THEN '部门小计' ELSE team END AS team, SUM(salary) AS total FROM staff GROUP BY ROLLUP(dept, team);
注意两点:GROUPING() 必须作用于 GROUP BY 中的列;若对同一列混用 COALESCE 或 IFNULL 替换 NULL,会把真实为 NULL 的原始数据也误标为小计,必须用 GROUPING() 才可靠。
ROLLUP 看似一行语法,但字段顺序、NULL 含义、数据库版本、标签处理这四点漏掉任一,结果就容易出偏差——尤其是把 GROUPING() 当成可选技巧,实际它是区分真假 NULL 的唯一安全方式。










