CUBE 和 ROLLUP 之间的具体区别

本文深入解析SQL中的CUBE和ROLLUP运算符的区别,通过具体实例展示了如何使用它们生成不同层次的数据聚合结果,以及在多维数据分析中的实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CUBE 和 ROLLUP 之间的具体区别

ROLLUP 运算符生成的结果集类似于 CUBE 运算符生成的结果集。

下面是 CUBE 和 ROLLUP 之间的具体区别:

  • CUBE 生成的结果集显示了所选列中值的所有组合的聚合。
  • ROLLUP 生成的结果集显示了所选列中值的某一层次结构的聚合。

ROLLUP 优点:

  •  
    • (1)ROLLUP 返回单个结果集,而 COMPUTE BY 返回多个结果集,而多个结果集会增加应用程序代码的复杂性。
    • (2)ROLLUP 可以在服务器游标中使用,而 COMPUTE BY 则不可以。
    • (3)有时,查询优化器为 ROLLUP 生成的执行计划比为 COMPUTE BY 生成的更为高效。
    •  

下面对比一下GROUP BY 、CUBE 和  ROLLUP后的结果

创建表:

CREATE TABLE DEPART 
(部门 char(10),员工 char(6),工资 int)

INSERT INTO DEPART SELECT 'A','ZHANG',100 
INSERT INTO DEPART SELECT 'A','LI',200 
INSERT INTO DEPART SELECT 'A','WANG',300 
INSERT INTO DEPART SELECT 'A','ZHAO',400 
INSERT INTO DEPART SELECT 'A','DUAN',500 
INSERT INTO DEPART SELECT 'B','DUAN',600 
INSERT INTO DEPART SELECT 'B','DUAN',700

部门         员工         工资

A             ZHANG     100 
A             LI             200 
A             WANG      300 
A             ZHAO      400 
A             DUAN      500 
B             DUAN      600 
B             DUAN      700

 

(1)GROUP BY 

SELECT 部门,员工,SUM(工资)AS TOTAL 
from DEPART 
GROUP BY 部门,员工

结果:

A             DUAN      500 
B             DUAN      1300 
A             LI        200 
A             WANG      300 
A             ZHANG     100 
A             ZHAO      400

(2)ROLLUP

SELECT 部门,员工,SUM(工资)AS TOTAL 
from DEPART 
GROUP BY  部门,员工  WITH ROLLUP

结果如下:

A             DUAN       500 
A             LI             200 
A             WANG      300 
A             ZHANG     100 
A             ZHAO       400 
A             NULL        1500 
B             DUAN       1300 
B             NULL       1300 
NULL       NULL        2800

ROLLUP结果集中多了三条汇总信息:即部门A的合计,部门B的合计以及总合计。其中将部门B中的DUAN合计。

等价于下列SQL语句

SELECT 部门,员工,SUM(工资)AS TOTAL 
from DEPART 
GROUP BY 部门,员工 
union 
SELECT 部门,'NULL',SUM(工资)AS TOTAL 
from DEPART 
GROUP BY  部门 
union 
SELECT 'NULL','NULL',SUM(工资)AS TOTAL 
from DEPART

结果:

A             DUAN      500 
A             LI           200 
A             NULL      1500 
A             WANG      300 
A             ZHANG     100 
A             ZHAO       400 
B             DUAN      1300 
B             NULL       1300 
NULL       NULL        2800

(3)CUBE

SELECT 部门,员工,SUM(工资)AS TOTAL 
from DEPART 
GROUP BY 部门,员工 WITH CUBE

结果:

A             DUAN      500 
A             LI           200 
A             WANG      300 
A             ZHANG     100 
A             ZHAO      400 
A             NULL      1500 
B             DUAN      1300 
B             NULL      1300 
NULL    NULL         2800 
NULL    DUAN        1800 
NULL    LI               200 
NULL    WANG       300 
NULL    ZHANG       100 
NULL    ZHAO         400

CUBE的结果集是在 ROLLUP结果集的基础上多了5行,这5行相当于在ROLLUP结果集上在union 上以员工 (即CUBE)为 GROUP BY的结果。

SELECT 部门,员工,SUM(工资)AS TOTAL 
from DEPART 
GROUP BY 部门,员工 WITH CUBE

等价于下列的SQL语句:

SELECT 部门,员工,SUM(工资)AS TOTAL 
from DEPART 
GROUP BY  部门,员工  WITH ROLLUP

union

SELECT 'NULL',员工,SUM(工资)AS TOTAL 
from DEPART 
GROUP BY 员工

结果:

NULL    NULL    2800 
A             NULL    1500 
A             DUAN      500 
A             LI        200 
A             WANG      300 
A             ZHANG     100 
A             ZHAO      400 
B             NULL    1300 
B             DUAN      1300 
NULL    DUAN      1800 
NULL    LI             200 
NULL    WANG      300 
NULL    ZHANG     100 
NULL    ZHAO      400

 

****************************************************************

rollup 举例

****************************************************************

这里介绍sql server2005里面的一个使用实例:

CREATE TABLE tb(province nvarchar(10),city nvarchar(10),score int)

INSERT tb SELECT '陕西','西安',3

UNION ALL SELECT '陕西','安康',4

UNION ALL SELECT '陕西','汉中',2

UNION ALL SELECT '广东','广州',5

UNION ALL SELECT '广东','珠海',2

UNION ALL SELECT '广东','东莞',3

UNION ALL SELECT '江苏','南京',6

UNION ALL SELECT '江苏','苏州',1

GO

1、 只有一个汇总

select province as 省,sum(score) as 分数 from tb group by province with rollup

结果:

广东 10

江苏 7

陕西 9

NULL 26

select case when grouping(province)=1 then '合计' else province end as 省,sum(score) as 分数 from tb group by province with rollup

结果:

广东 10

江苏 7

陕西 9

合计 26

2、两级,中间小计最后汇总

select province as 省,city as 市,sum(score) as 分数 from tb group by province,city with rollup

结果:

广东 东莞 3

广东 广州 5

广东 珠海 2

广东 NULL 10

江苏 南京 6

江苏 苏州 1

江苏 NULL 7

陕西 安康 4

陕西 汉中 2

陕西 西安 3

陕西 NULL 9

NULL NULL 26

select province as 省,city as 市,sum(score) as 分数,grouping(province) as g_p,grouping(city) as g_c from tb group by province,city with rollup

结果:

广东 东莞 3 0 0

广东 广州 5 0 0

广东 珠海 2 0 0

广东 NULL 10 0 1

江苏 南京 6 0 0

江苏 苏州 1 0 0

江苏 NULL 7 0 1

陕西 安康 4 0 0

陕西 汉中 2 0 0

陕西 西安 3 0 0

陕西 NULL 9 0 1

NULL NULL 26 1 1

select case when grouping(province)=1 then '合计' else province end 省,

case when grouping(city)=1 and grouping(province)=0 then '小计' else city end 市,

sum(score) as 分数

from tb group by province,city with rollup

结果:

广东 东莞 3

广东 广州 5

广东 珠海 2

广东 小计 10

江苏 南京 6

江苏 苏州 1

江苏 小计 7

陕西 安康 4

陕西 汉中 2

陕西 西安 3

陕西 小计 9

合计 NULL 26

 

****************************************************************

cube举例

****************************************************************

使用cube操作符时,最多可以有10个分组表达式

在cube中不能使用all关键字

举例

例如,简单表 Inventory 包含下列数据:

Item                 Color                Quantity                  

-------------------- -------------------- --------------------------

Table                Blue                 124                       

Table                Red                  223                       

Chair                Blue                 101                        

Chair                Red                  210                       

以下查询将返回一个结果集,其中包含 DE>ItemDE> 和 DE>ColorDE> 的所有可能组合的 DE>QuantityDE> 小计:

SELECT Item, Color, SUM(Quantity) AS QtySum

FROM Inventory

GROUP BY Item, Color WITH CUBE

下面是结果集:

Item                 Color                QtySum                    

-------------------- -------------------- --------------------------

Chair                Blue                 101.00                    

Chair                Red                  210.00                    

Chair                (null)               311.00                    

Table                Blue                 124.00                    

Table                Red                  223.00                    

Table                (null)               347.00                    

(null)               (null)               658.00                    

(null)               Blue                 225.00                    

(null)               Red                  433.00                    

我们着重考查结果集中的以下几行:

Chair                (null)               311.00                    

此行报告了在 DE>ItemDE> 维度中包含 DE>ChairDE> 值的所有行的小计。对 DE>ColorDE> 维度返回了 DE>nullDE> 值,用以表示该行报告的聚合包括 DE>ColorDE> 维度为任意值的行。

Table                (null)               347.00                    

这一行类似,但报告的是 DE>ItemDE> 维度中包含 DE>TableDE> 值的所有行的小计。

(null)               (null)               658.00                    

这一行报告了多维数据集的总计。DE>ItemDE> 和 DE>ColorDE> 维度都包含 DE>nullDE> 值。这表示此行中汇总了这两个维度的所有值。

(null)               Blue                 225.00                    

(null)               Red                  433.00                    

这两行报告了 DE>ColorDE> 维度的小计。两行中的 DE>ItemDE> 维度值都是 DE>nullDE>,表示聚合数据来自 DE>ItemDE> 维度为任意值的行。

  使用 GROUPING 区分空值

CUBE 操作生成空值将会带来一个问题:如何区分 CUBE 操作生成的 NULL 值和在实际数据中返回的 NULL 值?可以使用 GROUPING 函数解决此问题。如果列值来自事实数据,GROUPING 函数将返回 0;如果列值是由 CUBE 操作生成的 NULL,则返回 1。在 CUBE 操作中,生成的 NULL 代表所有值。可以编写 SELECT 语句以使用 GROUPING 函数将生成的任一 NULL 替换为字符串 ALL。由于事实数据中的 NULL 表示数据值未知,因此也可以将 SELECT 编码为返回字符串 UNKNOWN,用于表示事实数据中的 NULL。例如:

SELECT CASE WHEN (GROUPING(Item) = 1) THEN 'ALL'

            ELSE ISNULL(Item, 'UNKNOWN')

       END AS Item,

       CASE WHEN (GROUPING(Color) = 1) THEN 'ALL'

            ELSE ISNULL(Color, 'UNKNOWN')

       END AS Color,

       SUM(Quantity) AS QtySum

FROM Inventory

GROUP BY Item, Color WITH CUBE

  多维数据集

CUBE 运算符可用于生成 n 维的多维数据集,即具有任意维数的多维数据集。只有一个维度的多维数据集可用于生成合计,例如:

SELECT CASE WHEN (GROUPING(Item) = 1) THEN 'ALL'

            ELSE ISNULL(Item, 'UNKNOWN')

       END AS Item,

       SUM(Quantity) AS QtySum

FROM Inventory

GROUP BY Item WITH CUBE

GO

此 DE>SELECTDE> 语句返回的结果集既显示了 DE>ItemDE> 中每个值的小计,也显示了 DE>ItemDE> 中所有值的总计:

Item                 QtySum                    

-------------------- --------------------------

Chair                311.00                     

Table                347.00                    

ALL                  658.00                    

包含具有多个维度的 CUBE 的 SELECT 语句可生成大型结果集,因为这些语句会为所有维度中各值的所有组合都生成相应的行。这些大型结果集包含的数据可能会过多而不易于阅读和理解。此问题的一种解决办法是将 DE>SELECTDE> 语句放入视图中:

CREATE VIEW InvCube AS

SELECT CASE WHEN (GROUPING(Item) = 1) THEN 'ALL'

            ELSE ISNULL(Item, 'UNKNOWN')

       END AS Item,

       CASE WHEN (GROUPING(Color) = 1) THEN 'ALL'

            ELSE ISNULL(Color, 'UNKNOWN')

       END AS Color,

       SUM(Quantity) AS QtySum

FROM Inventory

GROUP BY Item, Color WITH CUBE

然后即可用该视图来仅查询您感兴趣的维度值:

SELECT *

FROM InvCube

WHERE Item = 'Chair'

  AND Color = 'ALL'

 

Item                 Color                QtySum                    

-------------------- -------------------- --------------------------

Chair                ALL                  311.00                    

 

(1 row(s) affected)


 
  

oracle Rollup 和 Cube用法

Oracle的GROUP BY语句除了最基本的语法外,还支持ROLLUP和CUBE语句。

如果是ROLLUP(A, B, C)的话,首先会对(A、B、C)进行GROUP BY,然后对(A、B)进行GROUP BY,然后是(A)进行GROUP BY,最后对全表进行GROUP BY操作。

如果是GROUP BY CUBE(A, B, C),则首先会对(A、B、C)进行GROUP BY,然后依次是(A、B),(A、C),(A),(B、C),(B),(C),最后对全表进行GROUPBY操作。

grouping_id()可以美化效果:

Oracle的GROUP BY语句除了最基本的语法外,还支持ROLLUP和CUBE语句。

用GROUP BY GROUPING SETS来代替GROUP BY CUBE。你可以应用来指定你感兴趣的总数组合。因为它不必计算它不需要集合(也不会产生太多结果),所以对SQL引擎来说更为高效。 
其格式为: 
GROUP BY GROUPING SETS ((list), (list) ... ) 
这里(list)是圆括号中的一个列序列,这个组合生成一个总数。要增加一个总和,必须增加一个(NUlL)分组集。


SQL> createtable t as select * from dba_indexes;

表已创建。

 

SQL> selectindex_type, status, count(*) from t group by index_type, status;

INDEX_TYPE STATUS COUNT(*)

--------------------------- ------------------

LOB VALID 51

NORMAL N/A 25

NORMAL VALID 479

CLUSTER VALID 11

 

下面来看看ROLLUP和CUBE语句的执行结果。

SQL> select index_type,status, count(*) from t group by rollup(index_type, status);

INDEX_TYPE STATUS COUNT(*)

--------------------------- ------------------

LOB VALID 51

LOB 51

NORMAL N/A 25

NORMAL VALID 479

NORMAL 504

CLUSTER VALID 11

CLUSTER 11

566

已选择8行。

 

SQL> selectindex_type, status, count(*) from t group by cube(index_type, status);

INDEX_TYPE  STATUS  COUNT(*)

----------------------------------- ----------

566

N/A 25

VALID 541

LOB 51

LOB VALID 51

NORMAL 504

NORMAL N/A 25

NORMAL VALID 479

CLUSTER 11

CLUSTER VALID 11

已选择10行。

 

查询结果不是很一目了然,下面通过Oracle提供的函数GROUPING来整理一下查询结果。

SQL> selectgrouping(index_type) g_ind, grouping(status) g_st, index_type, status, count(*)

2 from t group byrollup(index_type, status) order by 1, 2;

G_IND G_ST INDEX_TYPESTATUS COUNT(*)

-------------------- --------------------------- -------- ----------

0 0 LOB VALID 51

0 0 NORMAL N/A 25

0 0 NORMAL VALID 479

0 0 CLUSTER VALID11

0 1 LOB 51

0 1 NORMAL 504

0 1 CLUSTER 11

1 1 566

已选择8行。

 

这个查询结果就直观多了,和不带ROLLUP语句的GROUP BY相比,ROLLUP增加了对INDEX_TYPE的GROUP BY统计和对所有记录的GROUP BY统计。

也就是说,如果是ROLLUP(A, B, C)的话,首先会对(A、B、C)进行GROUP BY,然后对(A、B)进行GROUP BY,然后是(A)进行GROUP BY,最后对全表进行GROUP BY操作。

 

下面看看CUBE语句。

 

SQL> selectgrouping(index_type) g_ind, grouping(status) g_st, index_type, status, count(*)

2 from t group bycube(index_type, status) order by 1, 2;

 

G_IND G_ST INDEX_TYPE STATUS COUNT(*)

---------- ------------------------------------- -------- ----------

0 0 LOB VALID 51

0 0 NORMALN/A 25

0 0 NORMALVALID 479

0 0 CLUSTER VALID 11

0 1 LOB 51

0 1 NORMAL504

0 1 CLUSTER 11

1 0 N/A 25

1 0 VALID 541

1 1 566

已选择10行。

 

和ROLLUP相比,CUBE又增加了对STATUS列的GROUP BY统计。

如果是GROUP BY CUBE(A, B, C),则首先会对(A、B、C)进行GROUP BY,然后依次是(A、B),(A、C),(A),(B、C),(B),(C),最后对全表进行GROUPBY操作。

 

除了使用GROUPING函数,还可以使用GROUPING_ID来标识GROUP BY结果。

 

SQL> selectgrouping_id(index_type, status) g_ind, index_type, status, count(*)

2 from t group byrollup(index_type, status) order by 1;

G_IND INDEX_TYPE STATUS COUNT(*)

---------- ----------------------------------- ----------

0 LOB VALID 51

0 NORMALN/A 25

0 NORMALVALID 479

0 CLUSTER VALID 11

1 LOB 51

1 NORMAL504

1 CLUSTER 11

3 566

已选择8行。

 

SQL> selectgrouping_id(index_type, status) g_ind, index_type, status, count(*)  2 from t group by cube(index_type, status)order by 1;

G_IND INDEX_TYPE STATUS COUNT(*)

---------- ----------------------------------- ----------

0 LOB VALID 51

0 NORMALN/A 25

0 NORMALVALID 479

0 CLUSTER VALID 11

1 LOB 51

1 NORMAL504

1 CLUSTER 11

2 N/A 25

2 VALID 541

3 566

已选择10行。

 

grouping_id()可以美化效果:

 

selectDECODE(GROUPING_ID(C1), 1, '合计', C1) D1,

DECODE(GROUPING_ID(C1,C2), 1, '小计', C2) D2,

DECODE(GROUPING_ID(C1,C2, C1 + C2), 1, '小计', C1 + C2) D3,

count(*),

GROUPING_ID(C1,C2, C1 + C2, C1 + 1, C2 + 1),

GROUPING_ID(C1)

from T2

group byrollup(C1, C2, C1 + C2, C1 + 1, C2 + 1);

 

===========================================================

 

1.报表合计专用的Rollup函数

销售报表

广州 1月 2000元

广州 2月 2500元

广州 4500元

深圳 1月 1000元

深圳 2月 2000元

深圳 3000元

所有地区 7500元

 

以往的查询SQL:

Selectarea,month,sum(money) from SaleOrder group by area,month

然后广州,深圳的合计和所有地区合计都需要在程序里自行累计

1.其实可以使用如下SQL:

Selectarea,month,sum(total_sale) from SaleOrder group by rollup(area,month)

就能产生和报表一模一样的纪录

 

2.如果year不想累加,可以写成

Selectyear,month,area,sum(total_sale) from SaleOrder group by year,rollup(month,area)

另外Oracle 9i还支持如下语法:

Selectyear,month,area,sum(total_sale) from SaleOrder group byrollup((year,month),area)

 

3.如果使用Cube(area,month)而不是RollUp(area,month),除了获得每个地区的合计之外,还将获得每个月份的合计,在报表最后显示。

 

4.Grouping让合计列更好读

RollUp在显示广州合计时,月份列为NULL,但更好的做法应该是显示为"所有月份"

Grouping就是用来判断当前Column是否是一个合计列,1为yes,然后用Decode把它转为"所有月份"

Select Decode(Grouping(area),1,'所有地区',area) area, Decode(Grouping(month),1,'所有月份',month), sum(money) FromSaleOrder Group by RollUp(area,month);

 

2.对多级层次查询的start with.....connect by

比如人员组织,产品类别,Oracle提供了很经典的方法

SELECT LEVEL, name, emp_id,manager_emp_id FROM employee START WITHmanager_emp_id is null CONNECT BY PRIOR emp_id = manager_emp_id;

 

上面的语句demo了全部的应用,start with指明从哪里开始遍历树,如果从根开始,那么它的manager应该是Null,如果从某个职员开始,可以写成emp_id='11'

 

CONNECT BY 就是指明父子关系,注意PRIOR位置 。另外还有一个LEVEL列,显示节点的层次

 

3.更多报表/分析决策功能

3.1 分析功能的基本结构

分析功能() over( partion子句,order by子句,窗口子句)

概念上很难讲清楚,还是用例子说话比较好.

 

3.2 Row_Number 和 Rank, DENSE_Rank

 

用于选出Top 3 sales这样的报表

当两个业务员可能有相同业绩时,就要使用Rank和Dense_Rank

比如

金额 RowNum RankDense_Rank

张三 4000元 1 1 1

李四 3000元 2 2 2

钱五 2000元 3 3 3

孙六 2000元 4 3 3

丁七 1000元 5 5 4

这时,应该把并列第三的钱五和孙六都选进去,所以用Ranking功能比RowNumber保险.至于Desnse还是Ranking就看具体情况了。

SELECT salesperson_id, SUM(tot_sales) sp_sales, RANK( ) OVER (ORDERBY SUM(tot_sales) DESC) sales_rank FROM orders GROUP BY salesperson_id

 

3.3 NTILE 把纪录平分成甲乙丙丁四等

比如我想取得前25%的纪录,或者把25%的纪录当作同一个level平等对待,把另25%当作另一个Level平等对待

 

SELECT cust_nbr,SUM(tot_sales) cust_sales, NTILE(4) OVER (ORDER BY SUM(tot_sales) DESC)sales_quartile FROM orders GROUP BY cust_nbr ORDER BY 3,2 DESC;

NTITLE(4)把纪录以 SUM(tot_sales)排序分成4份.

 

3.4 辅助分析列和Windows Function

报表除了基本事实数据外,总希望旁边多些全年总销量,到目前为止的累计销量,前后三个月的平均销量这样的列来参考.

这种前后三个月的平均和到目前为止的累计销量就叫windowsfunction, 见下例

SELECT month, SUM(tot_sales) monthly_sales, SUM(SUM(tot_sales)) OVER(ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) max_preceedingFROM orders GROUP BY month ORDER BY month;

 

SELECT month, SUM(tot_sales) monthly_sales, AVG(SUM(tot_sales)) OVER(ORDER BY month ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) rolling_avg FROMorders GROUP BY month ORDER BY month;

 

Windows Function的关键就是Windows子句的几个取值

 

1 PRECEDING 之前的一条记录

1 FOLLOWING 之后的一条记录

UNBOUNDEDPRECEDING 之前的所有记录

CURRENT ROW 当前纪录

 

4.SubQuery总结

 

SubQuery天天用了,理论上总结一下.SubQuery 分三种

1.Noncorrelated 子查询 最普通的样式.

2.CorrelatedSubqueries 把父查询的列拉到子查询里面去,头一回cyt教我的时候理解了半天.

3.Inline View 也被当成最普通的样式用了.

然后Noncorrelated 子查询又有三种情况

1.返回一行一列 where price < (selectmax(price) from goods )

2.返回多行一列 where price>= ALL (selectprice from goods where type=2)

or where NOTprice< ANY(select price from goods where type=2)

最常用的IN其实就是=ANY()

 

3.返回多行多列 一次返回多列当然就节省了查询时间

UPDATE monthly_orders SET (tot_orders, max_order_amt) = (SELECTCOUNT(*), MAX(sale_price) FROM cust_order) DELETE FROM line_item WHERE(order_nbr, part_nbr) IN (SELECT order_nbr, part_nbr FROM cust_order c)

 

========================================

/*--------理解grouping sets

select a, b, c,sum( d ) from t  group by grouping sets (a, b, c )

等效于

select * from (

select a, null,null, sum( d ) from t group by a

union all

select null, b,null, sum( d ) from t group by b

union all

select null, null,c, sum( d ) from t group by c

)

*/

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值