GROUPING()返回1表示该列被ROLLUP/CUBE上卷的汇总行,0表示原始分组值;仅用于含ROLLUP/CUBE/GROUPING SETS的GROUP BY中,参数须为分组列名,不可在WHERE中使用,可配合CASE/HAVING/ORDER BY精准识别行类型。

GROUPING() 返回值的含义必须记牢
它不是判空函数,而是专用于识别 ROLLUP/CUBE/GROUPING SETS 生成的“占位 NULL”。返回 1 表示该列被上卷、当前行为汇总行;返回 0 表示该列为原始分组值——哪怕值本身是数据库存的 NULL,也属于明细层。
常见错误现象:WHERE dept IS NULL 会同时删掉部门小计行、全表总计行、以及所有真实 dept 为空的员工记录;COALESCE(dept, '小计') 则把三类 NULL 全标成“小计”,报表逻辑直接崩坏。
-
GROUPING(dept)在GROUP BY dept, team WITH ROLLUP中合法,在普通GROUP BY dept中会报错:GROUPING() can only be used in a GROUP BY clause with ROLLUP, CUBE or GROUPING SETS - 参数只能是
GROUP BY子句中**实际出现的列名或别名**,不能是UPPER(dept)、SUM(salary)或未参与分组的字段 - 返回类型为
tinyint,只取0或1,可直接用于CASE、HAVING、ORDER BY,但**不能出现在WHERE子句中**(执行时机早于分组)
多列 ROLLUP 下必须组合判断层级
单看一个 GROUPING() 不足以定位行类型。例如 GROUP BY ROLLUP(dept, team) 会产生三类行,需用两个函数联合判断:
-
GROUPING(dept) = 0 AND GROUPING(team) = 0→ 明细行(dept和team都有原始值) -
GROUPING(dept) = 0 AND GROUPING(team) = 1→ 部门小计行(team被上卷,dept仍有效) -
GROUPING(dept) = 1 AND GROUPING(team) = 1→ 全表总计行(两列均被上卷)
典型写法是嵌套 CASE 输出可读标签:
SELECT
CASE
WHEN GROUPING(dept) = 1 THEN '总计'
WHEN GROUPING(team) = 1 THEN dept + ' 小计'
ELSE dept
END AS dept_label,
CASE WHEN GROUPING(team) = 1 THEN NULL ELSE team END AS team,
COUNT(*) AS cnt
FROM staff
GROUP BY dept, team WITH ROLLUP
排序时要把 GROUPING() 结果纳入 ORDER BY
ROLLUP 不保证输出顺序。只写 ORDER BY dept, team 会让所有 team IS NULL 的汇总行堆在开头或结尾,脱离上下文。
正确做法是用 GROUPING() 控制层级先后:
- 让部门小计紧随该部门明细之后:按
dept升序,再按GROUPING(team)升序(0在前,1在后) - 让总计行固定在最末:加
GROUPING(dept) DESC(1排最前,但配合其他条件可压到末尾)
例如:
ORDER BY dept, GROUPING(team), team
就能实现“部门A明细 → 部门A小计 → 部门B明细 → 部门B小计 → 总计”的清晰结构。
GROUPING() 不能替代 WHERE 过滤,但可用在 HAVING 中
想排除所有汇总行?别用 WHERE team IS NOT NULL——它会误杀真实空值。正确方式是:
- 保留全部明细(含原始 NULL):加
HAVING GROUPING(team) = 0 AND GROUPING(dept) = 0 - 只取部门小计:用
HAVING GROUPING(dept) = 0 AND GROUPING(team) = 1 - 提取总计行:用
HAVING GROUPING(dept) = 1 AND GROUPING(team) = 1
注意:HAVING 执行在分组之后,此时 GROUPING() 已可用;而 WHERE 执行在分组之前,调用 GROUPING() 直接报错。
最容易被忽略的是:GROUPING() 的语义只针对“是否由 ROLLUP 生成”,和字段本身的 NULL 值完全无关。混用 IS NULL 和 GROUPING() 是统计口径错乱的根源。

















