Oracle的rollup、cube、grouping sets函數

Oracle的group by除了基本用法以外,還有3種擴展用法,分別是rollup、cube、grouping sets。

Oracle的rollup、cube、grouping sets函數

1 rollup

假設有一個表test,有A、B、C、D、E5列。

如果使用group by rollup(A,B,C),首先會對(A、B、C)進行GROUP BY,然後對(A、B)進行GROUP BY,然後是(A)進行GROUP BY,最後對全表進行GROUP BY操作。

roll up的意思是“捲起”,這也可以幫助我們理解group by rollup就是對選擇的列從右到左以一次少一列的方式進行grouping直到所有列都去掉後的grouping(也就是全表grouping),對於n個參數的rollup,有n+1次的grouping。以下2個sql的結果集是一樣的:

Select A,B,C,sum(E) from test group by rollup(A,B,C)

Select A,B,C,sum(E) from test group by A,B,C

union all

Select A,B,null,sum(E) from test group by A,B

union all

Select A,null,null,sum(E) from test group by A

union all

Select null,null,null,sum(E) from test

2 cube

cube的意思是立方,對cube的每個參數,都可以理解為取值為參與grouping和不參與grouping兩個值的一個維度,然後所有維度取值組合的集合就是grouping的集合,對於n個參數的cube,有2^n次的grouping。如果使用group by cube(A,B,C),,則首先會對(A、B、C)進行GROUP BY,然後依次是(A、B),(A、C),(A),(B、C),(B),(C),最後對全表進行GROUP BY操作,一共是2^3=8次grouping。同rollup一樣,也可以用基本的group by加上結果集的union all寫出一個與group by cube結果集相同的sql:

Select A,B,C,sum(E) from test group by cube(A,B,C);

Select A,B,C,sum(E) from test group by A,B,C

union all

Select A,B,null,sum(E) from test group by A,B

union all

Select A,null,C,sum(E) from test group by A,C

union all

Select A,null,null,sum(E) from test group by A

union all

Select null,B,C,sum(E) from test group by B,C

union all

Select null,B,null,sum(E) from test group by B

union all

Select null,null,C,sum(E) from test group by C

union all

Select null,null,null,sum(E) from test;

3 grouping sets

grouping sets就是對參數中的每個參數做grouping,也就是有幾個參數做幾次grouping,

例如使用group by grouping sets(A,B,C),則對(A),(B),(C)進行group by,如果使用group by grouping sets((A,B),C),則對(A,B),(C)進行group by。甚至grouping by grouping set(A,A)都是語法允許的,也就是對(A)進行2次group by,

grouping sets的參數允許重複

4 總結

rollup (N+1個分組方案)

cube (2^N個分組方案)

grouping sets (自定義羅列出分組方案)

5 注意點

5.1 機制不同

在rollup和cube的說明中分別給出了用基本group by加結果集union all給出了結果集相同的sql,但這只是為了理解的方便而給出的sql,並不說明rollup和cube與基本group by加結果集union all等價。實際上兩者的內部機制是完全不一樣的,前者除了寫法簡潔以外,運行時不需多次掃描表,效率遠比後者高。

5.2 集合可運算

3種擴展用法的參數可以是源表中的某一個具體的列,也可以是若干列經過計算而形成的一個新列(比如說A+B,A||B),也可以是這兩種列的一個集合(例如(A+B,C)),對於grouping set更是特殊,可以是空集合(),表示對全表進行group by。

5.3 group by 與 rollup, cube組合使用

(*3)Group by的基本用法以及這3種擴展用法可以組合使用,也就是說可以出現group by A,rollup(A,B)這樣的用法,oracle將對出現在group by中的每種用法的grouping列集合做笛卡爾積然後對其中的每一個元素做group by。這話說起來挺繞口,舉例說明吧:

group by A, rollup(A,B):

--基本用法的grouping集合是(A),

--rollup(A,B)的grouping集合是((A,B),(A),()),

--兩個集合的笛卡爾積集合是((A,A,B),(A,A),(A)),所以會首先對(A,A,B)做group by,然後對(A,A)做group by,最後對(A)做group by。

--實際上對(A,A,B)做group by和對(A,B)做group by兩者是完全等價的(group by A,A,B結果和group by A,B完全一樣),同理對(A,A)做group by和對(A)做group by也是等價的。

--簡化後的結果就是首先對(A,B)做group by,然後對(A)做group by,最後再對(A)做group by。下面給出兩個等價的sql以便理解:

Select A,B,sum(E) from test1 group by A, rollup(A,B);

Select A,B,sum(E) from test1 group by A,B

Union all

Select A,null,sum(E) from test1 group by A

Union all

Select A,null,sum(E) from test1 group by A;

6 grouping()、grouping_id()、group_id()

grouping_id()可以美化效果

6.1 grouping()

參數只有一個,而且必須為group by中出現的某一列,表示結果集的一行是否對該列做了grouping。對於對該列做了grouping的行而言,grouping()=0,反之為1;

6.2 grouping_id()

參數可以是多個,但必須為group by中出現的列。Grouping_id()的返回值其實就是參數中的每列的grouping()值的二進制向量,例如如果grouping(A)=1,grouping(B)=0,則grouping_id(A,B)的返回值就是二進制的10,轉成10進制就是2。

6.3 group_id()

無參數。見上面的說明(*3),group by對某些列的集合會進行重複的grouping,而實際上絕大多數情況下對結果集中的這些重複行是不需要的,那就必須有辦法剔出這些重複grouping的行。當結果集中有n條重複grouping而形成的行時,每行的group_id()分別是0,1,…,n,這樣我們在條件中加入一個group_id()<1就可以剔出這些重複grouping的行了。

7 示例

7.1 建表與數據

SQL> create table test(department_id number, a varchar2(20), b varchar2(20));

Table created

SQL> insert into test values(10, 'A', 'B');

1 row inserted

SQL> commit;

Commit complete

7.2 查詢語句

select department_id,

a,

b,

grouping(department_id),

grouping(a),

grouping(b)

from test

group by rollup(department_id, a, b)

order by 4, 5, 6;

select department_id,

a,

b,

grouping(department_id),

grouping(a),

grouping(b)

from test

group by cube(department_id, a, b)

order by 4, 5, 6;

其它:

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

select ... from 表 group by rollup(.....) having grouping_id(....)<=1

總計是grouping_id=3

小計grouping_id=1

記錄是grouping_id=0

grouping_id中括號裡面的字段和rollup括號裡面的字段一致就行

追問

大俠,你好請問grouping_id是腫麼來的,

回答

oracle的函數,針對rollup和cube的

追問

大俠,你好,灰常感謝,我的問題解決了,只是還要向您討教一下,那個grouping_id的數字是怎麼確定的,假如我rollup(...)裡面有兩個列,那麼grouping_id(..)的最大值是多少呢,假如是三列呢。。。望請賜教。

回答

向量值,可以認為是二進值的數,例:grouping_id(a,b,c),a為空就是0非空為1,b和c也一樣,結果會得到一個三位數,用二進制轉換成十進制就是了,例:a,b,c全是非空,即111就是7,三列就是7,如果是兩列自然是11就是3了

案例:

SQL> create table t as select * from dba_indexes;

表已創建。

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

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

INDEX_TYPE

STATUS

COUNT(*)

LOB

N/A

1

IOT - TOP

VALID

72

CLUSTER

VALID

10

DOMAIN

VALID

2

NORMAL

VALID

3439

LOB

VALID

231

NORMAL

N/A

79

FUNCTION-BASED NORMAL

VALID

30

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

已選擇8行

下面來看看ROLLUP和CUBE語句的執行結果。

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

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

INDEX_TYPE

STATUS

COUNT(*)

LOB

N/A

1

LOB

VALID

231

LOB

null

232

DOMAIN

VALID

2

DOMAIN

null

2

NORMAL

N/A

79

NORMAL

VALID

3439

NORMAL

null

3518

CLUSTER

VALID

10

CLUSTER

null

10

IOT - TOP

VALID

72

IOT - TOP

null

72

FUNCTION-BASED NORMAL

VALID

30

FUNCTION-BASED NORMAL

null

30

null

null

3864

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

已選擇15行。

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

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

INDEX_TYPE

STATUS

COUNT(*)

null

null

3864

null

N/A

80

null

VALID

3784

LOB

null

232

LOB

N/A

1

LOB

VALID

231

DOMAIN

null

2

DOMAIN

VALID

2

NORMAL

null

3518

NORMAL

N/A

79

NORMAL

VALID

3439

CLUSTER

null

10

CLUSTER

VALID

10

IOT - TOP

null

72

IOT - TOP

VALID

72

FUNCTION-BASED NORMAL

null

30

FUNCTION-BASED NORMAL

VALID

30

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

已選擇17行。

查詢結果不是很一目瞭然,下面通過Oracle提供的函數GROUPING來整理一下查詢結果。

SQL> select grouping(index_type), grouping(status), index_type, status, count(*)

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

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

GROUPING(INDEX_TYPE)

GROUPING(STATUS)

INDEX_TYPE

STATUS

COUNT(*)

0

0

DOMAIN

VALID

2

0

0

IOT - TOP

VALID

72

0

0

LOB

N/A

1

0

0

CLUSTER

VALID

10

0

0

LOB

VALID

231

0

0

NORMAL

N/A

79

0

0

NORMAL

VALID

3439

0

0

FUNCTION-BASED NORMAL

VALID

30

0

1

IOT - TOP

72

0

1

CLUSTER

10

0

1

DOMAIN

2

0

1

LOB

232

0

1

NORMAL

3518

0

1

FUNCTION-BASED NORMAL

30

1

1

3864

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

已選擇15行。

這個查詢結果就直觀多了,和不帶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>

select grouping(index_type) , grouping(status), index_type, status, count(*) from t group by cube(index_type, status) order by 1, 2;

G_IND

G_ST

INDEX_TYPE

STATUS

COUNT(*)

0

0

NORMAL

N/A

79

0

0

FUNCTION-BASED NORMAL

VALID

30

0

0

NORMAL

VALID

3439

0

0

IOT - TOP

VALID

72

0

0

LOB

N/A

1

0

0

LOB

VALID

231

0

0

CLUSTER

VALID

10

0

0

DOMAIN

VALID

2

0

1

DOMAIN

2

0

1

LOB

232

0

1

NORMAL

3518

0

1

FUNCTION-BASED NORMAL

30

0

1

IOT - TOP

72

0

1

CLUSTER

10

1

0

N/A

80

1

0

VALID

3784

1

1

3864

已選擇17行。

和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),最後對全表進行GROUP BY操作。

除了使用GROUPING函數,還可以使用GROUPING_ID來標識GROUP BY結果。

SQL> select grouping_id(index_type, status) g_ind, index_type, status, count(*)

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

G_IND

INDEX_TYPE

STATUS

COUNT(*)

0

DOMAIN

VALID

2

0

IOT - TOP

VALID

72

0

LOB

N/A

1

0

CLUSTER

VALID

10

0

LOB

VALID

231

0

NORMAL

N/A

79

0

NORMAL

VALID

3439

0

FUNCTION-BASED NORMAL

VALID

30

1

IOT - TOP

72

1

CLUSTER

10

1

DOMAIN

2

1

LOB

232

1

NORMAL

3518

1

FUNCTION-BASED NORMAL

30

3

3864

已選擇15行。

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

G_IND

INDEX_TYPE

STATUS

COUNT(*)

0

NORMAL

N/A

79

0

FUNCTION-BASED NORMAL

VALID

30

0

NORMAL

VALID

3439

0

IOT - TOP

VALID

72

0

LOB

N/A

1

0

LOB

VALID

231

0

CLUSTER

VALID

10

0

DOMAIN

VALID

2

1

DOMAIN

2

1

LOB

232

1

NORMAL

3518

1

FUNCTION-BASED NORMAL

30

1

IOT - TOP

72

1

CLUSTER

10

2

N/A

80

2

VALID

3784

3

3864

已選擇17行。

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

grouping_id()可以美化效果:

select

DECODE(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 by rollup(C1, C2, C1 + C2, C1 + 1, C2 + 1);

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

1.報表合計專用的Rollup函數

銷售報表

廣州 1月 2000元

廣州 2月 2500元

廣州小計 4500元

深圳 1月 1000元

深圳 2月 2000元

深圳小計 3000元

所有地區 7500元

以往的查詢SQL:

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

然後廣州,深圳的合計和所有地區合計都需要在程序裡自行累計

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

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

就能產生和報表一模一樣的紀錄

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

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

另外Oracle 9i還支持如下語法:

Select year,month,area,sum(total_sale) from SaleOrder group by rollup((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) From SaleOrder Group by RollUp(area,month);

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

2.對多級層次查詢的start with.....connect by

比如人員組織,產品類別,Oracle提供了很經典的方法

SELECT LEVEL, name, emp_id,manager_emp_id FROM employee START WITH manager_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 Rank Dense_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 (ORDER BY 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

報表除了基本事實數據外,總希望旁邊多些全年總銷量,到目前為止的累計銷量,前後三個月的平均銷量這樣的列來參考.

這種前後三個月的平均和到目前為止的累計銷量就叫windows function, 見下例

SELECT month, SUM(tot_sales) monthly_sales, SUM(SUM(tot_sales)) OVER (ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) max_preceeding FROM 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 FROM orders GROUP BY month ORDER BY month;

Windows Function的關鍵就是Windows子句的幾個取值

1 PRECEDING 之前的一條記錄

1 FOLLOWING 之後的一條記錄

UNBOUNDED PRECEDING 之前的所有記錄

CURRENT ROW 當前紀錄

4.SubQuery總結

SubQuery天天用了,理論上總結一下.SubQuery 分三種

1.Noncorrelated 子查詢 最普通的樣式.

2.Correlated Subqueries 把父查詢的列拉到子查詢裡面去,頭一回cyt教我的時候理解了半天.

3.Inline View 也被當成最普通的樣式用了.

然後Noncorrelated 子查詢又有三種情況

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

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

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

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

3.返回多行多列 一次返回多列當然就節省了查詢時間

UPDATE monthly_orders SET (tot_orders, max_order_amt) = (SELECT COUNT(*), 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)


分享到:


相關文章: