CUBE和ROLLUP都是SQL标准中GROUP BY子句的扩展运算符,用于在一条查询语句里同时输出明细和多个层级的汇总结果。不少人在初次接触时会疑惑:同样是三个字段的分组汇总,为什么用CUBE查出来的行数明显比ROLLUP多?这背后其实是两种运算符对维度组合的生成策略完全不同。ROLLUP走的是一条收敛路径,而CUBE走的是全组合。理解这一点,不仅能解释行数差异,还能帮助你在报表开发中做出正确的选择。

ROLLUP的层级收敛机制:只生成一条汇总路径
ROLLUP的设计初衷是模拟报表中的层级汇总,比如先按年份汇总,再按年份加地区汇总,最后按年份加地区加产品汇总。它的组合生成是从右往左逐级消去的:每消去一个维度,就多出一个更粗粒度的汇总行,直到全部维度被消去,得到总计行。
假设分组字段是(year, region, product),ROLLUP生成的组合依次是:
SELECT year, region, product, SUM(amount) AS total FROM sales GROUP BY ROLLUP (year, region, product); -- 生成的分组组合: -- (year, region, product) 最细粒度明细 -- (year, region) 按年+地区汇总 -- (year) 按年汇总 -- () 总计
可以看到,三个字段只产生了4种组合。推广到n个字段,ROLLUP的组合数量固定为n + 1个。它的特点是严格有序,字段位置决定了汇总层级,适合处理存在自然层级关系的维度,比如年份到月份到日、公司到部门到员工这类树状结构。
这也带来一个限制:ROLLUP永远不会生成跨层级的组合。它不会单独按地区汇总,也不会单独按产品汇总,因为在它看来维度是从左到右依次嵌套的层级,脱离层级的组合没有意义。
CUBE的全组合机制:对每个维度做独立汇总
CUBE的语义则完全不同,它把每个维度都当作相互独立的轴,生成所有可能的维度组合。对n个字段,每个字段都有出现或不出现两种状态,因此组合数量是2的n次方。
还是同样的三个字段(year, region, product),CUBE会生成:
SELECT year, region, product, SUM(amount) AS total FROM sales GROUP BY CUBE (year, region, product); -- 生成的分组组合(共 2^3 = 8 种): -- (year, region, product) -- (year, region) -- (year, product) -- (region, product) -- (year) -- (region) -- (product) -- ()
对比ROLLUP的4种,CUBE多出了(year, product)、(region, product)、(region)、(product)四种组合。这些多出来的行正是跨维度的交叉汇总,比如按地区和产品交叉统计,就能回答哪个地区哪类产品卖得最好这类问题,而ROLLUP无法直接给出。
组合数量的增长速度也值得警惕。n等于3时是8种,n等于4时是16种,n等于10时理论上会达到1024种。如果维度本身的取值基数也很大,CUBE的结果集可能急剧膨胀,查询耗时和内存占用都会明显上升。因此在维度较多时,通常改用GROUPING SETS精确指定需要的组合,而不是无脑上CUBE。
用一个公式看清行数差异
单看组合种数还不够直观,实际行数还取决于每个维度去重后的取值个数。假设year有3个取值、region有5个取值、product有10个取值,各维度近似均匀分布:
ROLLUP的行数约为 3×5×10 + 3×5 + 3 + 1 = 169 行,只计算层级路径上的四个组合各自的数据行数。而CUBE的行数约为 (3+1)×(5+1)×(10+1) = 264 行,因为每个维度相当于多了一个所有值合并后的桶。维度越多、基数越高,两者的差距会进一步拉大。
判断该用哪个运算符,可以套用一个简单的思路:如果维度之间有明确的层级或从属关系,报表只需要沿这条层级逐级上卷,用ROLLUP;如果需要做交叉分析,任意两个维度之间都可能要互相对照着看,用CUBE。下面是一个实际场景的对比:
-- 场景一:按年-月-日的销售上卷报表,层级清晰,用 ROLLUP SELECT year, month, day, SUM(amount) FROM sales GROUP BY ROLLUP (year, month, day); -- 场景二:多维分析,地区、产品、渠道互相交叉,用 CUBE SELECT region, product, channel, SUM(amount) FROM sales GROUP BY CUBE (region, product, channel); -- 场景三:只需要其中几种组合,用 GROUPING SETS 精确控制 SELECT region, product, SUM(amount) FROM sales GROUP BY GROUPING SETS ((region), (product), (region, product));
区分汇总行与数据行的技巧
ROLLUP和CUBE生成的汇总行中,被消去的维度会显示为NULL,这可能和数据本身存的NULL混淆。SQL提供了GROUPING()函数来区分:参数对应的字段如果是因为汇总而被置NULL,GROUPING返回1,否则返回0。
SELECT
year,
region,
SUM(amount) AS total,
GROUPING(year) AS gy,
GROUPING(region) AS gr
FROM sales
GROUP BY CUBE (year, region)
ORDER BY gy DESC, gr DESC;
-- gy=1, gr=1 的行是总计
-- gy=1, gr=0 的行是仅按 region 汇总配合GROUPING函数,还可以用CASE WHEN把NULL替换成更友好的显示文字,比如把总计行的年份列显示为“全部年份”,让报表的可读性更好。
总结一下:CUBE比ROLLUP组合更多的根本原因,是ROLLUP只沿n+1条层级路径生成汇总,而CUBE覆盖全部2的n次方种维度组合。前者服务于层级报表,后者服务于交叉分析,两者不是谁更强大的关系,而是面向不同的分析需求。理解了各自的组合生成逻辑,你就能在写多维统计SQL时准确预估结果规模,选出最合适的运算符。