grouping sets适合精确指定多个不连续分组维度(如(dept,city)、(channel)、(product_line)),避免rollup/cube自动补全中间层;它不支持mysql 8.4及以下,且必须用grouping()函数区分占位null与真实null。

GROUPING SETS 适合解决需要一次查询输出多个固定分组粒度、且各粒度之间无强制层级关系的聚合问题。它不是万能替代,用错场景反而更麻烦。
需要同时输出几个不连续的分组维度
比如业务要“按部门+城市汇总”、“只按渠道汇总”、“只按产品线汇总”,中间跳过“部门”或“渠道+产品线”这类组合——ROLLUP 和 CUBE 都会自动补全中间层,只有 GROUPING SETS 能精确列出 ((dept, city), (channel), (product_line)) 这种非连续组合。
- 错误做法:硬套
ROLLUP(dept, city, channel),结果多出几十行根本不用的(dept)、(dept, city)汇总 - 真实数据里如果
dept或channel本身有 NULL 值,ROLLUP生成的占位 NULL 会和真实 NULL 混在一起,无法区分 - 必须配合
GROUPING()判断,不能直接写WHERE dept IS NULL
MySQL 用户请立刻放弃这个念头
截至 MySQL 8.4(当前最新版),GROUPING SETS 仍不支持。执行会直接报错:ERROR 1064。别查文档、别试版本号,官方明确未实现。
- PostgreSQL 9.5+、SQL Server 2008+、Oracle 9i+、Trino/Presto 支持
- Hive/Spark SQL 支持,但注意 Hive 3.1+ 才稳定,旧版可能解析失败
- SQLite 和大部分 OLAP 引擎(如 ClickHouse)也不支持,别默认“标准 SQL 就该有”
NULL 是占位符,不是缺失值
结果中出现的 NULL 不代表原始数据为空,而是引擎为对齐不同分组维度自动填充的占位符。这是最容易踩坑的地方。
-
COALESCE(dept, 'ALL')会让GROUPING(dept)失效——函数只能作用于原始字段,替换后就再也分不清是真 NULL 还是占位了 - 想加标签?得在
SELECT里用CASE WHEN GROUPING(dept) = 1 THEN '总计' ELSE dept END,而不是先COALESCE再判断 - 排序时
ORDER BY dept NULLS FIRST在 PostgreSQL 有效,但在 SQL Server 中要用ORDER BY ISNULL(dept, '')等价写法
性能提升只在单次扫描有意义
GROUPING SETS 的价值在于避免多次全表扫描,但它不会让单次扫描变快。如果表没建索引、数据量极大、或聚合字段基数太高,它照样慢。
- 对高基数字段(如
user_id、order_no)用CUBE或误写成GROUPING SETS ((a,b,c), (a,b), (a), (b), (c), ()),结果集可能爆炸式增长,内存溢出 - 真正省时间的是减少 I/O,不是减少 CPU;若底层存储是列存(如 Parquet + Spark),扫描开销本就不大,收益会打折扣
- 某些数据库(如 PostgreSQL)对
ROLLUP有专属优化路径,等效写法下ROLLUP(a,b)反而比GROUPING SETS ((a,b),(a),())快一点











