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
)
*/