必须用 GROUPING() 函数,因为 IS NULL 无法区分 ROLLUP 自动生成的 NULL 占位符和原始数据中的真实 NULL;GROUPING(col)=1 表示该列是汇总行占位符,=0 表示真实分组值;需配合 ORDER BY GROUPING() 使用以保证层级顺序。

必须用 GROUPING() 函数,其他方式(如 IS NULL、COALESCE()、IFNULL())都无法可靠区分。
为什么 IS NULL 会失效
IS NULL 只看值是否为 NULL,不关心来源。ROLLUP 生成的汇总行里,被“卷起”的列(比如 city)会被主动置为 NULL;而原始数据中 city 字段本身就可能允许为空。两者在结果集中完全等价,WHERE city IS NULL 会同时捞出小计行和脏数据行——轻则报表多出错误标签,重则漏掉关键汇总层级。
常见错误现象包括:
- 把“华东区小计”和“地区未知的客户明细”都标成“合计”
- 用
ORDER BY city导致汇总行散落在中间,破坏层级顺序 -
HAVING COUNT(*) > 10过滤时误删了真实空值但数量大的明细组
GROUPING() 的参数和返回值怎么用
GROUPING() 接收一个分组列名作为参数,返回 0 或 1(SQL Server 返回 tinyint,MySQL/PG 返回 int):
-
GROUPING(city) = 1→ 这行的city是 ROLLUP 主动填的占位符,不是原始数据 -
GROUPING(city) = 0→city是真实分组值(哪怕它本身是NULL) - 参数必须是
GROUP BY子句中出现的列名,不能是计算字段或未参与分组的字段 - 在普通
GROUP BY(无WITH ROLLUP)中调用会直接报错:GROUPING() can only be used in a GROUP BY clause with ROLLUP, CUBE or GROUPING SETS
示例(SQL Server / MySQL 8.0+):
SELECT
CASE WHEN GROUPING(dept) = 1 THEN '总计'
WHEN GROUPING(team) = 1 THEN dept + ' 小计'
ELSE dept + '-' + team END AS label,
SUM(salary) AS total
FROM staff
GROUP BY dept, team WITH ROLLUP;
ORDER BY 和 GROUPING() 必须配合使用
ROLLUP 输出的行顺序不保证层级清晰,尤其当分组列含 NULL 值时,ORDER BY dept, team 会让汇总行插在明细中间。正确做法是用 GROUPING() 控制排序优先级:
-
ORDER BY GROUPING(dept), dept, GROUPING(team), team:先排非汇总行(GROUPING(dept)=0),再排dept值,然后是dept小计行(GROUPING(dept)=0 AND GROUPING(team)=1),最后是总计行(GROUPING(dept)=1) - 避免只按原始字段排序,否则
NULL值默认排最前或最后,打乱树状结构 - MySQL 中
GROUPING()在ORDER BY中可用;SQL Server 同样支持,但旧版本需确认兼容级别 ≥ 90
最容易被忽略的一点:GROUPING() 不是“补丁函数”,它是 ROLLUP 语义的组成部分。跳过它去硬套 COALESCE() 或前端判断,等于放弃 SQL 层级聚合的可控性——一旦原始表新增允许 NULL 的字段,报表逻辑就可能静默崩坏。

















