必须用 grouping() 函数判断 rollup 行,因 is null 无法区分真实空值与汇总占位符,导致统计错误;grouping() 仅在含 rollup/cube/grouping sets 的查询中有效,参数须为 group by 中实际列名,返回 0 或 1 表示是否为汇总行。

必须用 GROUPING() 函数判断 ROLLUP 行,直接用 IS NULL 会把真实空值和汇总占位符混为一谈,统计结果必然错乱。
为什么不能用 IS NULL 判断汇总行
ROLLUP 生成的小计/总计行会把上卷字段设为 NULL,但业务表里本来就可能存了合法的 NULL。比如 city 字段允许为空,WHERE city IS NULL 会同时干掉:部门小计行、全表总计行、以及所有城市信息缺失的真实订单——三类数据无法区分。
常见错误现象:
-
COALESCE(city, '小计')把所有city IS NULL都标成“小计”,原始空值和汇总行全被污染 - 导出报表后总数对不上,明细行数变少,小计消失
- 按
ORDER BY city排序时,汇总行被挤到开头或结尾,脱离上下文
GROUPING() 的合法使用位置和参数限制
GROUPING() 不是通用判空函数,它只在含 WITH ROLLUP、WITH CUBE 或 GROUPING SETS 的查询中有效。SQL Server 会硬性报错:GROUPING() can only be used in a GROUP BY clause with ROLLUP, CUBE or GROUPING SETS。
参数必须严格满足:
- 只能是
GROUP BY子句中**实际出现的列名或别名**(如GROUP BY region, city WITH ROLLUP→ 可用GROUPING(region)和GROUPING(city)) - 不能是计算列(如
GROUPING(UPPER(city)))、聚合字段(如GROUPING(SUM(amount)))、或未参与分组的字段 - 返回值是
tinyint:0 表示该列为原始分组值(哪怕值本身是NULL),1 表示该列是 ROLLUP 自动生成的占位符
多列 ROLLUP 中如何组合判断层级
对 GROUP BY ROLLUP(dept, team),单看一个 GROUPING() 不够,必须组合判断:
-
GROUPING(dept) = 0 AND GROUPING(team) = 0→ 正常明细行(dept + team 都有值) -
GROUPING(dept) = 0 AND GROUPING(team) = 1→ dept 小计行(team 被上卷,占位NULL) -
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(),不能靠 WHERE
GROUPING() 不能出现在 WHERE 子句里——因为 WHERE 执行在分组前,此时汇总占位符还没生成,调用直接报错。
正确做法:
- 过滤汇总行:用
HAVING GROUPING(team) = 0(保留明细层) - 只取总计行:用
HAVING GROUPING(dept) = 1 AND GROUPING(team) = 1 - 控制排序顺序:把
GROUPING()结果加入ORDER BY,例如ORDER BY dept, GROUPING(team), team,确保每个 dept 下的明细行在前、小计行紧随其后
最容易被忽略的一点:导出前检查所有 IS NULL 是否都已替换为 GROUPING(col) = 1 —— 混用会导致部分汇总行被意外过滤,且问题难以复现。











