擴展GROUP BY概述
在本章的開頭已經簡單描述了擴展GROUP BY的應用場景,Oracle擴展GROUP BY允許使用SQL語句對數據匯總結果進行多維展現,從而生成復雜的報表,為決策者提供有效的數據支持。最重要的是,Oracle9i之后,擴展GROUP BY的功能已經趨于完善,能夠滿足大部分多維數據分析統計的工作。
主要表現在:
本章的表DEPT和EMP都來自于SCOTT用戶下,雖然表比較簡單,但是足以能說明擴展GROUP BY的功能。下面開始進入主要內容,探討強大的擴展GROUP BY功能。
ROLLUP
假設有這樣的需求:
--需求1實現
SELECT a.dname,b.job,SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY a.dname,b.job
UNION ALL
--需求2實現
SELECT a.dname,NULL, SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY a.dname
UNION ALL
--需求3實現
SELECT NULL,NULL, SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno;
DNAME JOB SUM_SAL
---------------------------- ------------------ ----------
SALES MANAGER 2850
SALES CLERK 950
ACCOUNTING MANAGER 2450
ACCOUNTING PRESIDENT 5000
ACCOUNTING CLERK 1300
SALES SALESMAN 5600
RESEARCH MANAGER 2975
RESEARCH ANALYST 3000
RESEARCH CLERK 800
ACCOUNTING 8750
RESEARCH 6775
SALES 9400
24925
已選擇13行。
----------------------------------------------------------
Plan hash value: 3113041979
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17 | 423 | 13 (70)| 00:00:01 |
| 1 | UNION-ALL | | | | | |
| 2 | HASH GROUP BY | | 12 | 336 | 5 (20)| 00:00:01 |
| 3 | NESTED LOOPS | | 12 | 336 | 4 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | EMP | 12 | 180 | 3 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 13 | 1 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
| 7 | HASH GROUP BY | | 4 | 80 | 5 (20)| 00:00:01 |
| 8 | NESTED LOOPS | | 12 | 240 | 4 (0)| 00:00:01 |
| 9 | TABLE ACCESS FULL | EMP | 12 | 84 | 3 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 13 | 1 (0)| 00:00:01 |
|* 11 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
| 12 | SORT AGGREGATE | | 1 | 7 | | |
|* 13 | TABLE ACCESS FULL | EMP | 12 | 84 | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
6 - access("A"."DEPTNO"="B"."DEPTNO")
11 - access("A"."DEPTNO"="B"."DEPTNO")
13 - filter("B"."DEPTNO" IS NOT NULL)
從執行計劃可以看出,為了實現這樣的需求,需要多次訪問EMP、DEPT表以及DEPT表的索引,這里的測試數據很少而且表結構簡單,實際應用中表結構可能很復雜,經常是多表關聯,數據量可能達到百萬級,千萬級,甚至上億,那么使用UNION ALL,明顯性能低下,如果使用WITH子句將常規分組的結果固定下來,然后在此基礎上再聚合,效率可能比單純UNION ALL好,但是還是不夠完美,現在的需求是對2列進行多維分析,如果是很多列呢?必然增加語句復雜度,類似地,CUBE,GROUPING SETS用UNION ALL改寫也有此類問題,何況擴展GROUP BY還提供了很多復雜功能,用UNION ALL改寫就更加麻煩了。
試想對于上面的需求,如果Oracle能提供一個這樣的分組功能就好了:
2.2 ROLLUP分組
SELECT … GROUP BY ROLLUP(grouping_column_reference_list)
SELECT a.dname,b.job, SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY ROLLUP(a.dname,b.job);
DNAME JOB SUM_SAL
---------------------------- ------------------ ----------
SALES CLERK 950
SALES MANAGER 2850
SALES SALESMAN 5600
SALES 9400
RESEARCH CLERK 800
RESEARCH ANALYST 3000
RESEARCH MANAGER 2975
RESEARCH 6775
ACCOUNTING CLERK 1300
ACCOUNTING MANAGER 2450
ACCOUNTING PRESIDENT 5000
ACCOUNTING 8750
24925
已選擇13行。
----------------------------------------------------------
Plan hash value: 503922295
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 12 | 336 | 5 (20)| 00:00:01 |
| 1 | SORT GROUP BY ROLLUP | | 12 | 336 | 5 (20)| 00:00:01 |
| 2 | NESTED LOOPS | | 12 | 336 | 4 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | EMP | 12 | 180 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 13 | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("A"."DEPTNO"="B"."DEPTNO")
ROLLUP分組具有方向性,從上面的結果看出,ROLLUP(a.dname,b.job)分組的過程是這樣的:
分組級別 | 描述 |
a,b,c | 標準分組 |
a,b | 對于每個a,b列值,計算橫跨c列的小計 |
a | 對于每個a列值,計算橫跨b,c列的小計 |
合計匯總 | 合計 |
另外提一下,其實ROLLUP操作,如果使用HINT: expand_gset_to_union,那么則優化器會將ROLLUP轉為對應的UNION ALL操作,其它的GROUPING SETS、CUBE也可以,有興趣的可以試一下。
ROLLUP語法簡單,而且具體處理過程也很簡單,除了第1個是標準分組,然后就是列從右到左遞減的分組,最后合計。下面實現需求:
SELECT to_char(b.hiredate,yyyy) hire_year,a.dname,b.job, SUM(sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY ROLLUP(to_char(b.hiredate,yyyy),a.dname,b.job);
HIRE_YEA DNAME JOB SUM_SAL
-------- ---------------------------- ------------------ ----------
1980 RESEARCH CLERK 800
1980 RESEARCH 800
1980 800
1981 SALES CLERK 950
1981 SALES MANAGER 2850
1981 SALES SALESMAN 5600
1981 SALES 9400
1981 RESEARCH ANALYST 3000
1981 RESEARCH MANAGER 2975
1981 RESEARCH 5975
1981 ACCOUNTING MANAGER 2450
1981 ACCOUNTING PRESIDENT 5000
1981 ACCOUNTING 7450
1981 22825
1982 ACCOUNTING CLERK 1300
1982 ACCOUNTING 1300
1982 1300
24925
相關行 | 描述 |
第7行 | 對第4、5、6行的所有JOB進行小計 |
第14行 | 對第7行、第13行的所有DNAME、JOB進行小計,當然也相當于對4-6、8-12行所有DNAME、JOB的小計。 |
最后一行 | 對所有入職日期(精確到年)、DNAME、JOB進行合計 |
因為ROLLUP分組過程具有方向性,所以通過改變ROLLUP中列的順序就可以達到改變報表結果和含義的目的。比如將前面的ROLLUP(dname,job)改為ROLLUP(job,dname)則含義就發生了變化,現在需要查詢的就是標準分組、計算每個job的所有部門的小計、最后合計,這里就兩個列,也就是小計的含義發生了變化,請看:
SELECT b.job,a.dname, SUM(b.sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY ROLLUP(b.job,a.dname);
JOB DNAME SUM_SAL
------------------ ---------------------------- ----------
CLERK SALES 950
CLERK RESEARCH 800
CLERK ACCOUNTING 1300
CLERK 3050
ANALYST RESEARCH 3000
ANALYST 3000
MANAGER SALES 2850
MANAGER RESEARCH 2975
MANAGER ACCOUNTING 2450
MANAGER 8275
SALESMAN SALES 5600
SALESMAN 5600
PRESIDENT ACCOUNTING 5000
PRESIDENT 5000
24925
已選擇15行。
SELECT to_char(b.hiredate,yyyy) hire_year,a.dname,b.job, SUM(sal) sum_sal
FROM dept a,emp b
WHERE a.deptno = b.deptno
GROUP BY to_char(b.hiredate,yyyy),a.dname,ROLLUP(b.job);
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/129114.html
摘要:當向傳入一列時,會得到一個總計行。結果當向傳遞兩列時,將會按照這兩列進行分組,同時按照第一列的分組結果返回小計行。結果可以看出來結果是按照工廠和部門分別分組匯總的。選擇的就表示兩列都不為空。 Group By Group By 誰不會啊?這不是最簡單的嗎?越是簡單的東西,我們越會忽略掉他,因為我們不愿意再去深入了解它。1 小時 SQL 極速入門(一)1 小時 SQL 極速入門(二)1 ...
閱讀 1346·2023-01-11 13:20
閱讀 1684·2023-01-11 13:20
閱讀 1132·2023-01-11 13:20
閱讀 1858·2023-01-11 13:20
閱讀 4100·2023-01-11 13:20
閱讀 2704·2023-01-11 13:20
閱讀 1385·2023-01-11 13:20
閱讀 3597·2023-01-11 13:20