GROUPING SETS能替代多个UNION ALL,且只需一次全表扫描,执行计划仅含一个HashAggregate节点;但MySQL 8.4不支持,PostgreSQL 9.5+、SQL Server 2008+等支持;须用GROUPING()函数区分占位NULL与真实NULL,不可用COALESCE。

GROUPING SETS 能不能替代多个 UNION ALL?
能,而且必须替代——只要数据库支持。它把原本要扫表 3~5 次的查询压缩成 1 次全表扫描,执行计划里只出现一个 HashAggregate 或 GroupAggregate 节点。但注意:MySQL 8.4 仍不支持 GROUPING SETS,硬写会报 ERROR 1064;PostgreSQL 9.5+、SQL Server 2008+、Trino、Presto、Hive、SparkSQL 都可用。
怎么写才不会把 NULL 当成真实数据?
所有被折叠的维度列都会显示为 NULL,但这和原始数据里的 NULL 完全不同。直接用 WHERE dept IS NULL 过滤,会同时干掉“部门小计行”和“部门字段本就为空的记录”。必须依赖 GROUPING() 函数:
-
GROUPING(dept)返回1→ 该行未按dept分组,dept的NULL是占位符 -
GROUPING(dept)返回0→dept有真实值(或原始值就是NULL) - 展示层用
CASE WHEN GROUPING(dept) = 1 THEN '总计' ELSE dept END,别用COALESCE(dept, '总计')
GROUPING SETS 和 ROLLUP/CUBE 该怎么选?
看业务维度有没有天然层级关系:
- 要「年→季度→月→城市」这种树状上卷,用
ROLLUP(year, quarter, month, city),等价于GROUPING SETS ((year,quarter,month,city), (year,quarter,month), (year,quarter), (year), ()) - 要「所有两两组合 + 单维 + 全局」,比如分析
region和channel的交叉影响,用CUBE(region, channel) - 只要其中几组特定组合(比如只要「部门+城市」「部门」「总计」,不要「城市」单独一行),必须用
GROUPING SETS—— 它是唯一能精确控制输出集合的语法
为什么加了 COUNT(DISTINCT) 就变慢甚至 OOM?
因为 GROUPING SETS 不做计算复用:每组分组都独立跑一遍 COUNT(DISTINCT),中间结果可能膨胀数倍。比如 GROUPING SETS ((a,b), (a), (b)) 中,a 的去重集合会被算 3 次。
实操建议:
- 优先预聚合:先用子查询对
user_id做DISTINCT去重,再外层GROUPING SETS - 避免在宽分组集里混用多个
COUNT(DISTINCT),尤其当维度 > 3 时 - Hive/SparkSQL 可考虑
approx_count_distinct()换性能,误差率通常
最易被忽略的一点:索引必须覆盖最宽的那个分组组合。比如 GROUPING SETS ((a,b,c), (a), (b)),优化器大概率走 (a,b,c) 复合索引,如果只有 (a) 单列索引,性能反而更差。

















