grouping sets 是 sql 标准中的分组语法而非函数,用于单次扫描生成多维度聚合结果;它需显式声明维度元组列表(如 (a,b), (a), ()),配合 grouping() 函数区分占位 null 与真实 null,mysql 8.4 仍不支持,postgresql 9.5+、sql server 2008+ 等主流数据库支持。

GROUPING SETS 不是函数,不能加括号写成 GROUPING_SETS();它是 SQL 标准中的分组语法,用于在单次扫描中产出多个指定维度组合的聚合结果。MySQL(含 8.4)至今不支持,直接执行会报 ERROR 1064;PostgreSQL 9.5+、SQL Server 2008+、Oracle 12c+、Trino/Presto、SparkSQL、Hive 2.3.0+、MaxCompute 均可用。
GROUPING SETS 的括号里到底写什么
括号内是「维度元组」的列表,每个元组用小括号包裹,逗号分隔,末尾不能有逗号。它不是列名列表,也不是表达式,而是明确声明“这一行聚合按哪些列分组”:
-
(dept, region)→ 按部门和地区联合分组(最细粒度) -
(dept)→ 只按部门分组(地区列值为 NULL 占位) -
(region)→ 只按地区分组(部门列值为 NULL 占位) -
()→ 零维度分组,即全表总计(所有分组列均为 NULL 占位)
错误写法:GROUP BY GROUPING SETS (dept, region)(缺小括号)、GROUPING SETS (dept, region, ())(前两项没包成元组)、GROUPING SETS ((dept, region, product))(多写了 product 但表里没有该字段,报错)。
为什么必须配合 GROUPING() 函数才能用
所有非当前分组维度的字段,在结果中统一显示为 NULL。你无法靠肉眼区分:某行的 dept = NULL 是因为“按地区汇总”,还是“全表总计”,抑或是“原始数据里真有 dept 为 NULL 的记录”。GROUPING() 是唯一可靠判别方式:
-
GROUPING(dept) = 0→ 该行参与了dept分组(值非占位) -
GROUPING(dept) = 1→dept列在此行为占位符(真实 NULL 或未参与分组) - 组合判断示例:
GROUPING(dept)=1 AND GROUPING(region)=0→ 这是按region单独分组的小计行
仅用 COALESCE(dept, 'ALL') 展示友好标签可以,但逻辑分支、BI 下钻、权限过滤等必须依赖 GROUPING() 返回值,否则会把真实 NULL 数据误判为汇总行。
ROLLUP 和 CUBE 能替代 GROUPING SETS 吗
不能无条件替代。三者生成的组合集合数学上不同:
-
ROLLUP(a,b,c)等价于GROUPING SETS ((a,b,c),(a,b),(a),())—— 强制前缀层级,不能跳过b直接要(a,c) -
CUBE(a,b)等价于GROUPING SETS ((a,b),(a),(b),())—— 全排列,但维度数一多就爆炸(CUBE(a,b,c,d)产生 16 种组合) -
GROUPING SETS是精确控制:你要哪几个就写哪几个,不多不少,不隐含、不遗漏、不冗余
典型误用场景:想出“按年+城市”和“按季度+城市”两个汇总,却用 ROLLUP(year, quarter, city),结果多出 4 层无用组合,还混入了 (year, quarter) 这种不需要的中间层。
实际写法中容易被忽略的细节
很多人写出语法正确的 GROUPING SETS 查询,但上线后报表数据对不上——问题往往不在主干逻辑,而在这些地方:
- SELECT 列表中所有非聚合字段,必须出现在某个
GROUPING SETS元组里;否则报错或结果不可控(如写SELECT dept, region, product, SUM(sales),但GROUPING SETS ((dept), (region))中没包含product) - Hive/MaxCompute 中
GROUPING_ID()返回整型掩码,比逐个调用GROUPING()更紧凑;但 PostgreSQL 不支持GROUPING_ID(),必须手拼GROUPING(a)*4 + GROUPING(b)*2 + GROUPING(c) - 空元组
()必须显式写出,不能省略;漏掉它,就拿不到全表总计行 - 某些引擎(如旧版 Hive)对
GROUPING()在WHERE子句中的使用有限制,建议在 HAVING 或外层 SELECT 中做判断
最常被跳过的动作:没在最终输出里用 GROUPING() 做行语义标记。光靠 NULL 值展示,等于把解释权交给下游应用,风险极高。










