rollup适合明确层级维度(如年→月→日),cube适合穷举交叉组合(如地区×品类×客户类型),二者均须配合grouping()识别汇总行,否则null易被误判为真实缺失值。

直接说结论:ROLLUP 适合有明确层级关系的维度(比如年→月→日),CUBE 适合需要穷举所有交叉组合的分析场景(比如地区×品类×客户类型),但二者都必须配合 GROUPING() 才能安全识别汇总行,否则 NULL 容易被误判为真实缺失值。
ROLLUP 的层级逻辑和字段顺序不能颠倒
ROLLUP 不是简单地“多加几行总计”,它严格按 GROUP BY ROLLUP(a, b, c) 中的列顺序生成嵌套汇总:(a,b,c) → (a,b) → (a) → ()。一旦把 region 放在最后却想先看全国小计,结果就全乱了。
- 错误写法:
GROUP BY ROLLUP(product, region)想先得各 region 总计,实际先出的是各 product 下的 region 小计 - 正确做法:按分析目标排顺序,比如要“区域→产品→时间”,就写
ROLLUP(region, product, sale_date) - ORDER BY 必须用前缀子集,例如
ORDER BY region, product可以,ORDER BY product会打乱层级可读性
CUBE 生成的行数爆炸,别盲目套用三列以上
CUBE(a,b,c) 实际等价于 GROUPING SETS((a,b,c),(a,b),(a,c),(b,c),(a),(b),(c),()),共 2³ = 8 组合。加到 4 列就是 16 行,5 列 32 行——还没算数据量本身。
- 常见误用:对高基数列(如
user_id、order_id)用CUBE,查询直接卡死或 OOM - 实用建议:只对业务上真正需要交叉分析的低基数维度用 CUBE,比如
CUBE(region, product_category, customer_segment) - 性能提示:PostgreSQL 9.5+ 虽支持,但 CUBE 结果不保证顺序,必须显式
ORDER BY,且无法用索引加速分组过程
NULL 是占位符,不是脏数据:必须用 GROUPING() 判断
ROLLUP/CUBE 返回的 NULL 是 PostgreSQL 主动填入的汇总标记,不代表原始数据缺失。直接 WHERE region IS NOT NULL 会把所有汇总行全干掉。
- 安全写法:用
GROUPING(region)返回 1 表示该行中region是汇总占位(即 NULL 是计算出来的),返回 0 表示真实值 - 可读性增强:配合
COALESCE(region, '(all regions)')或CASE WHEN GROUPING(region) = 1 THEN '(total)' ELSE region END - 注意:
GROUPING()参数必须和 GROUP BY 中的列名完全一致,不能是别名或表达式
GROUPING SETS 比 ROLLUP/CUBE 更灵活,但语法稍冗长
当你要的不是完整层级或全组合,而是几个特定分组(比如只要“按部门”和“按职级”,不要“部门+职级”也不想要总计),GROUPING SETS 是唯一选择。
- 示例:
GROUP BY GROUPING SETS ((dept), (job_level), ())明确只产出三类:部门汇总、职级汇总、全局总计 - ROLLUP 和 CUBE 都是 GROUPING SETS 的语法糖,底层全走同一套执行引擎
- 调试技巧:先把 GROUPING SETS 写清楚,再看是否能简化成 ROLLUP/CUBE;反过来则不一定可行
最常被忽略的一点:ROLLUP/CUBE 的聚合函数(如 SUM()、COUNT())在每一行都独立计算,但如果你在 SELECT 里混用窗口函数或子查询,它们不会自动适配分组层级——该加 OVER () 还得加,该提前过滤还得在 CTE 里做。










