GROUPING SETS通过单次扫描对每行数据按各组set重复计算聚合值,导致中间结果膨胀,行数≈原表行数×sets数量,开销取决于sets数量、维度基数及聚合函数类型。

GROUPING SETS 是 SQL 中实现多维度组合聚合的高效语法,它比多次 UNION ALL 更简洁、比 CUBE/ROLLUP 更精准,但会带来额外的存储与计算开销——关键在于理解其执行逻辑与数据膨胀机制。
GROUPING SETS 如何生成多组聚合结果
它不是“分别执行多个 GROUP BY”,而是在一次扫描中,对同一行数据按各组 set 重复计算聚合值。例如:
SELECT dept, region, SUM(sales)
FROM sales
GROUP BY GROUPING SETS ((dept), (region), ());
对每一行记录,引擎会同时尝试归入三个分组:仅按 dept、仅按 region、以及空分组(全表总计)。每匹配一个 set,就生成一条中间聚合行。若某行 dept='A'、region='CN',它将贡献 3 条中间结果:(dept='A', NULL, sum1)、(NULL, 'CN', sum2)、(NULL, NULL, sum3)。这种“一数多投”是空间放大的根源。
存储开销主要来自中间结果集膨胀
最终结果行数 ≈ 原表行数 × grouping sets 数量(在无重复键时的上界)。实际膨胀程度取决于:
- GROUPING SETS 的数量:每增加一个 set,潜在中间行数线性增长
- 各维度的基数分布:低基数列(如 status IN ('Y','N'))导致大量 NULL 填充行聚集,加剧 I/O 和内存压力
- 聚合函数类型:COUNT(*) 膨胀固定;SUM/AVG 需保留原始数值参与多轮计算,中间状态更重
优化空间开销的实用策略
不必回避 GROUPING SETS,但需有意识控制其“爆炸半径”:
- 优先用 WHERE 过滤再聚合:在 GROUP BY 前缩小输入行集,比在膨胀后过滤更省资源
- 拆分高基数 + 低基数组合:避免把 country(高基数)和 flag(低基数)放在同一 GROUPING SETS 列表中;可先按 country 聚合,再用 UNION ALL 补充 flag 维度统计
- 利用 GROUPING() 函数识别 NULL 来源:在 SELECT 中加入 GROUPING(dept), GROUPING(region),能区分真实 NULL 和占位 NULL,便于后续逻辑处理,减少冗余字段存储
- 评估是否真需全部组合:如果只关心 (a,b) 和 (a),用 GROUPING SETS ((a,b),(a)) 即可,不必写成 CUBE(a,b) 引入 (b) 和 () 等无关分组
对比 UNION ALL 与 GROUPING SETS 的实际开销差异
表面上 UNION ALL 更“直观”,但现代优化器通常能将其重写为单次扫描;而 GROUPING SETS 在语义明确的前提下,往往获得更好执行计划(如共享排序、复用哈希表)。真正差异在结果集结构:UNION ALL 各子句独立输出,列顺序/类型必须严格一致,且无法用 GROUPING() 区分来源;GROUPING SETS 输出统一结构,天然支持多维分析场景(如 BI 工具自动识别层级)。存储开销上,两者在最坏情况下接近,但 GROUPING SETS 更易被向量化执行器优化,CPU 利用率通常更高。










