GROUPING()函数专用于区分ROLLUP生成的聚合NULL(返回1)与原始数据NULL(返回0),其参数必须是GROUP BY列表中的列,且仅在含ROLLUP/CUBE/GROUPING SETS的查询中有效,否则报错。

GROUPING() 函数怎么识别 ROLLUP 生成的 NULL?
ROLLUP 产生的 NULL 和真实数据里的 NULL 在结果集里长得一模一样,但语义完全不同:前者是“这一层聚合的占位符”,后者是“这个字段确实没值”。GROUPING() 就是专干这事的——它只对 ROLLUP(或 CUBE、GROUPING SETS)自动补上的 NULL 返回 1,对原始数据里的 NULL 返回 0。
关键点在于:GROUPING() 的参数必须是 GROUP BY 列表里出现过的列名,且只能用于含 ROLLUP / CUBE 的查询中,否则会报错 ERROR: grouping() can only be used in a GROUP BY clause with ROLLUP, CUBE or GROUPING SETS。
-
GROUPING(col)返回INT:0 表示该行该列是真实值(含原始NULL),1 表示这是 ROLLUP 自动生成的聚合占位NULL - 如果列参与了多级 ROLLUP(比如
GROUP BY a, b WITH ROLLUP),GROUPING(b)为 1 时,b是空的,但a可能还有值;GROUPING(a)为 1 时,说明整行是最高层总计(a和b都空) - 不能对非
GROUP BY列调用GROUPING(),例如SELECT GROUPING(c) FROM t GROUP BY a WITH ROLLUP会直接报错
为什么不能直接用 IS NULL 判断 ROLLUP 的空值?
因为 IS NULL 拦不住原始数据里的 NULL。比如用户表里 city 字段本就存了 NULL,和 ROLLUP 生成的“全国汇总”级 city IS NULL 混在一起,根本分不清哪行是真缺失、哪行是聚合占位。
典型翻车场景:做报表时想把 ROLLUP 的汇总行标成“合计”,结果把所有 city IS NULL 的用户记录也标进去了——页面上突然冒出几百条“合计 张三 25 岁”这种荒谬数据。
- 错误写法:
CASE WHEN city IS NULL THEN '合计' ELSE city END→ 无法区分来源 - 正确写法:
CASE WHEN GROUPING(city) = 1 THEN '合计' ELSE COALESCE(city, '[未知]') END -
COALESCE(city, '[未知]')保留原始NULL的业务含义,GROUPING(city) = 1才触发汇总标识
GROUPING_ID() 和多个 GROUPING() 并用有啥区别?
GROUPING_ID(a, b, c) 是把多个 GROUPING() 结果当二进制位拼起来算的整数,本质是压缩写法。比如 GROUPING_ID(a,b) 等价于 GROUPING(a) * 2 + GROUPING(b)。
实际用不用它,取决于你是否需要快速分类聚合层级。比如 GROUP BY a, b, c WITH ROLLUP 会产生 8 种组合,GROUPING_ID(a,b,c) 的返回值从 0(全明细)到 7(全汇总)刚好对应二进制 000~111。
- 查“只按 a 汇总”的行:
GROUPING_ID(a,b,c) = 6(即110,a=0, b=1, c=1) - 但可读性差,出错难调试;多数情况下用
GROUPING(a)=0 AND GROUPING(b)=1 AND GROUPING(c)=1更直白 - MySQL 5.7+ 支持
GROUPING(),但不支持GROUPING_ID();PostgreSQL 和 SQL Server 都支持两者
GROUPING() 在不同数据库里的行为差异
函数名和基础逻辑一致,但细节兼容性得盯紧。最常踩的坑是 PostgreSQL 要求 GROUPING() 必须出现在 SELECT 或 HAVING 中,且不能在子查询里提前计算(会报 GROUPING is not allowed here);而 MySQL 相对宽松些。
- PostgreSQL:
GROUPING()只能在最外层 SELECT 或 HAVING 里用,不能出现在WHERE子句(得挪到HAVING) - MySQL:
GROUPING()可用于WHERE,但注意 8.0 以前版本不支持,会提示FUNCTION grouping does not exist - SQL Server:支持
GROUPING()和GROUPING_ID(),且允许在ORDER BY里用,方便把汇总行固定排在最后 - 所有数据库中,
GROUPING()对非 ROLLUP/CUBE 查询都报错,不是返回 NULL 或 0
真正容易被忽略的是:GROUPING() 的结果依赖于 GROUP BY 子句的**确切顺序和结构**。换掉 ROLLUP 位置、加个括号、或者混用 GROUPING SETS,都可能让 GROUPING() 的返回值含义整个变掉——别凭印象写,每次都要对着执行计划或小样本结果验证一遍。










