CUBE是GROUPING SETS的语法糖,穷举n个字段所有2ⁿ种分组组合;GROUPING SETS需手动指定分组元组,不自动补全;ROLLUP适用于层级维度,CUBE不感知语义关系。

GROUPING SETS 是显式枚举,CUBE 是 2ⁿ 全子集自动生成
CUBE(a, b, c) 本质就是 GROUPING SETS((a,b,c), (a,b), (a,c), (b,c), (a), (b), (c), ()) 的语法糖,它不推理、不筛选,只做穷举。你写 CUBE 就等于告诉数据库:“把这 n 个字段所有是否参与分组的组合都算一遍”。而 GROUPING SETS 要求你手动列出每个想要的分组元组,比如 GROUPING SETS((a,b), (c), ()) —— 它不会多出 (a) 或 (b,c),也不会漏掉你没写的组合。
常见错误是以为 GROUPING SETS((a), (b), (a,b)) 等价于 ROLLUP(a,b),其实不是:ROLLUP(a,b) 还会包含 ()(全表汇总),而 GROUPING SETS 不会,除非你显式写上 ()。
- CUBE 顺序无关:GROUP BY CUBE(a,b) 和 GROUP BY CUBE(b,a) 结果一致
- GROUPING SETS 顺序影响可读性,但不影响语义;括号内逗号分隔的列视为一个原子单元,如 GROUPING SETS((a,b), c) 表示“按 a+b 分组”和“按 c 分组”两组
- 两者都不能嵌套函数表达式直接进 CUBE 列表,比如 CUBE(YEAR(order_date)) 会报错,得先在 SELECT 或子查询里提取为别名
用 CUBE 前必须检查维度基数,否则结果行数爆炸
CUBE 生成 2ⁿ 行聚合结果(n 是字段数),但实际输出行数还取决于各维度的 DISTINCT 值数量。比如 CUBE(region, status, category) 中,region 有 5 个值、status 有 3 个、category 有 50 个,理论最大组合数是 2³ × 5 × 3 × 50 = 6000 行;但如果 category 是 user_id,哪怕只取前 100 个,2³ × 5 × 3 × 100 = 12000 行已可能拖慢查询或触发内存溢出。
真实场景中,CUBE 很容易返回远超预期的行数,尤其当某列含大量 NULL 或高基数时。此时看执行计划没用,得先探查数据分布:
- 运行
SELECT COUNT(DISTINCT region), COUNT(DISTINCT status), COUNT(DISTINCT category) FROM sales快速评估 - 避免把
order_id、user_id、timestamp这类字段放进 CUBE,哪怕加了 WHERE 过滤也不行——聚合计算仍会全量扫描 - 在 PostgreSQL 或 SQL Server 中,可用
LIMIT 10快速看 CUBE 结构,但注意 LIMIT 在聚合后生效,不减少计算量
区分原始 NULL 和 CUBE 生成的 NULL,必须用 GROUPING()
CUBE 输出中某列为 NULL,可能是该维度未参与分组(逻辑占位),也可能是原始数据本来就是 NULL。这两者业务含义完全不同,但表现一样。不加 GROUPING() 函数,你根本无法判断 region IS NULL 的那行到底是“全国汇总”,还是“region 字段缺失的脏数据”。
GROUPING(region) 返回 1 表示这一行的 region 是 CUBE 自动生成的占位值(即该维度被折叠),返回 0 表示它是真实值(哪怕值本身是 NULL)。
- 务必在 SELECT 列表中包含
GROUPING(col),尤其当需要导出报表或对接 BI 工具时 - 用
COALESCE(region, 'ALL')替换逻辑 NULL 时,要同步用GROUPING(region) = 0过滤条件保护原始 NULL 不被误标为 'ALL' -
GROUPING_ID(a,b,c)可压缩多个 GROUPING() 调用,比如GROUPING_ID(region,product)返回 0/1/2/3,对应二进制 00/01/10/11,直接映射分组模式
CUBE 不适合层级关系,该用 ROLLUP 的别硬套
CUBE 对字段间语义完全无感,它只做笛卡尔组合。如果你的维度有天然层级(比如 time: year → quarter → month,或 org: dept → team → person),用 CUBE 会产出大量无业务意义的组合,例如 (year, month) 有值,但 (quarter) 却为空——因为 CUBE 不保证前缀连续性。
这时候 ROLLUP(year, quarter, month) 才是正解:它按顺序生成 (year,quarter,month) → (year,quarter) → (year) → (),天然匹配树状汇总路径。把顺序写反成 ROLLUP(month, quarter, year),结果就只剩 (month)、(month,quarter)、(month,quarter,year)、(),彻底丢失季度和年度独立汇总。
- ROLLUP 性能通常略优于等效的 GROUPING SETS,在 PostgreSQL 和 SQL Server 中优化器对 ROLLUP 有专属路径
- MySQL 8.0.12+ 支持 ROLLUP/CUBE,但 WHERE 条件无法下推到 ROLLUP 子分组,可能扫更多行
- 如果既要层级汇总又要交叉维度(比如时间 + 地区),应拆成 ROLLUP(time_cols), CUBE(region_cols),而不是一股脑塞进一个 CUBE
GROUPING() 校验、不查基数、不区分 NULL 类型,基本等于埋了个定时查询雪崩。

















