必须用 GROUPING() 函数区分 ROLLUP 生成的占位 NULL 和原始 NULL,它仅适用于含 ROLLUP/CUBE/GROUPING SETS 的 GROUP BY 查询,参数须为分组列,返回 tinyint 值,常配合 CASE 使用,不可在 WHERE 中使用。

直接用 IS NULL 判断 ROLLUP 生成的空值会出错——它无法区分“原始数据本来就是 NULL”和“这是汇总行占位符”。必须用 GROUPING() 函数,它专为这事设计,返回 0 或 1,语义明确。
GROUPING() 只能用于含 ROLLUP/CUBE/GROUPING SETS 的 GROUP BY 查询
如果在普通 GROUP BY(没加 WITH ROLLUP)里调用 GROUPING(col),SQL Server 会立刻报错:GROUPING() can only be used in a GROUP BY clause with ROLLUP, CUBE or GROUPING SETS。这不是语法警告,是硬性限制。
-
GROUPING()的参数必须是GROUP BY子句中实际出现的列名(或别名),不能是计算列、聚合字段或未参与分组的字段 - 哪怕列本身在原始数据里全非 NULL,只要它出现在
GROUP BY ... WITH ROLLUP中,GROUPING(col)就合法 - 在
HAVING或ORDER BY中也能用GROUPING(),但不能在WHERE中——因为 WHERE 执行早于分组,此时聚合占位符还没生成
GROUPING(col) 返回 1 表示该列为 ROLLUP 自动生成的占位 NULL
比如 GROUP BY region, city WITH ROLLUP,结果里会出现四种组合:region+city 明细、region 级小计(city 为 NULL)、总计行(region 和 city 都为 NULL)。这时:
-
GROUPING(city) = 1→ 该行是 region 小计或总计行,city列的 NULL 是占位符 -
GROUPING(region) = 1→ 该行是总计行,region列的 NULL 是占位符(此时city必然也为 NULL,且GROUPING(city)也是 1) -
GROUPING(city) = 0时,city列值可能是字符串、数字,也可能是原始数据里的 NULL——GROUPING()不管这个,只管“是不是 ROLLUP 造的”
典型写法是配合 CASE 把占位行标清楚:CASE WHEN GROUPING(city) = 1 THEN 'City Total' ELSE city END。
别用 WHERE city IS NOT NULL 过滤汇总行
这条语句看着合理,实则危险:它会把所有 city 为 NULL 的行都干掉,包括真正的汇总行,也包括原始数据里 city 字段本就为 NULL 的明细行。结果是统计口径被破坏,小计消失,总数对不上。
- 正确做法是用
GROUPING(city) = 0来保留明细层(含原始 NULL),用GROUPING(city) = 1单独提取汇总层 - 若只想看明细(不含任何汇总),应写
HAVING GROUPING(city) = 0 AND GROUPING(region) = 0,而不是WHERE - 导出报表前,务必检查是否混用了
GROUPING()和IS NULL——前者是逻辑判断,后者是值判断,目标完全不同
最容易被忽略的一点:GROUPING() 的返回值是 tinyint,不是布尔。在需要参与算术运算(比如拼接标识字符串)或传给某些 BI 工具时,显式转成 INT 或 VARCHAR 更稳妥,避免隐式转换引发意外截断或类型不匹配。

















