grouping函数用于区分rollup/cube中汇总行的null占位符与真实null值,返回1表示该列被聚合(占位null),返回0表示原始数据(含真实null);必须配合group by使用且参数须为分组列。

GROUPING函数返回值的含义必须理解清楚
GROUPING 是一个只在 GROUP BY 配合 ROLLUP、CUBE 或 GROUPING SETS 时才有意义的函数。它接收一个列名作为参数,返回 1 或 0:当该列在当前分组行中是“被聚合掉的空值”(即小计/总计行中人为填充的 NULL),就返回 1;如果是原始数据中真实的 NULL 或非空值对应的分组,则返回 0。
关键点在于:数据库无法靠 IS NULL 判断这个 NULL 是数据本身还是汇总占位符——GROUPING 就是为解决这个歧义而生的。
用CASE配合GROUPING标记小计行最稳妥
直接在 SELECT 中对需要区分的列套一层 CASE WHEN GROUPING(col) = 1 THEN '小计' ELSE col END,能清晰分离语义。比如按部门和岗位做 ROLLUP:
SELECT
CASE WHEN GROUPING(dept) = 1 THEN '公司总计'
WHEN GROUPING(pos) = 1 THEN CONCAT(dept, '小计')
ELSE dept END AS dept_label,
CASE WHEN GROUPING(pos) = 1 THEN NULL ELSE pos END AS pos,
COUNT(*) AS cnt
FROM emp
GROUP BY dept, pos WITH ROLLUP;
注意:GROUPING(pos) = 1 表示这一行是「dept 维度的小计」,此时 pos 列的 NULL 是占位符,不是真实缺失;而 GROUPING(dept) = 1 表示已滚到顶层,是全表总计。
- 不要只依赖
IS NULL做判断,真实数据里pos完全可能存NULL -
GROUPING的参数必须是GROUP BY子句中出现的列,否则报错(如GROUPING(salary)在未按salary分组时非法) - MySQL 5.7+、PostgreSQL 9.5+、SQL Server、Oracle 都支持,但 SQLite 不支持
GROUPING_ID可以简化多列组合判断
当 GROUP BY 有三列以上并用 CUBE 时,逐个写 GROUPING(c1)=1 AND GROUPING(c2)=0... 很累。GROUPING_ID(c1,c2,c3) 返回一个整数,其二进制位对应各列的 GROUPING 值(高位到低位),比如 GROUPING_ID(a,b,c)=5 即 101b → a 和 c 被聚合、b 保留。
它本质是快捷写法,等价于:GROUPING(a)*4 + GROUPING(b)*2 + GROUPING(c)*1(三列时权重为 4/2/1)。
实际用时建议先用 GROUPING 单列调试清楚逻辑,再换 GROUPING_ID 精简表达式,避免位序搞反。
真实 NULL 和汇总 NULL 混在一起时最容易出错
这是最常踩的坑:开发看到结果里某行 pos IS NULL,就默认是小计,结果上线后发现某些员工岗位真是空的,导致统计口径污染。
务必坚持两个动作:
- 所有涉及
ROLLUP/CUBE的查询,对可能为NULL的维度列,都显式用GROUPING(col)做分支 - 如果业务上「岗位为空」有特殊含义(比如待分配),应在源数据或 ETL 阶段统一转成非空标识(如
'待定'),而不是留NULL和汇总占位符共存
GROUPING 不是语法糖,它是语义安全的必要开关。漏掉它,小计和脏数据就永远分不清。










