.Net程式員學用Oracle系列(21):分組查詢(GROUP BY)

来源:http://www.cnblogs.com/hanzongze/archive/2017/03/15/Oracle-Group-By.html
-Advertisement-
Play Games

1、 " GROUP BY 標準分組 " 1.1、 "GROUP BY 概述" 1.2、 "WHERE 和 HAVING 的區別?" 2、 " GROUP BY 擴展分組 " 2.1、 "ROLLUP 分組" 2.2、 "CUBE 分組" 2.3、 "GROUPING SETS 分組" 2.4、 " ...


1、GROUP BY 標準分組

為了便於講述,我將把簡單 GROUP BY 子句的分組稱之為標準分組,把 GROUP BY 子句中出現的列(或表達式)稱之為分組列。

1.1、GROUP BY 概述

在分組查詢中,GROUP BY 子句的作用就是按指定的(一或多個)列或表達式的值將選定行集進行分組,並針對每一組返回一行從組中收集到的數據。基本語法:

SELECT expression1, expression2, ... expression_n, 
       aggregate_function (aggregate_expression)
FROM tables
[WHERE conditions]
GROUP BY expression1, expression2, ... expression_n
[HAVING having_condition];

簡單示例:

SELECT t.dept_code,
  MAX(t.post_salary) max_salary,  -- 部門最高崗位工資
  MIN(t.post_salary) min_salary,  -- 部門最低崗位工資
  AVG(t.post_salary) avg_salary,  -- 部門平均崗位工資
  SUM(t.post_salary) sum_salary,  -- 部門崗位工資之和
  COUNT(t.post_salary) cnt_salary -- 部門工資份數
FROM demo.t_staff t
GROUP BY t.dept_code
HAVING AVG(t.post_salary)>3500
ORDER BY t.dept_code;

註意事項

  • 1、SELECT 子句中只能出現分組列或聚合函數或常量。
  • 2、HAVING 子句中只能出現分組列或聚合函數或常量。
  • 3、GROUP BY 子句中只能出現標量基元類型(如 VARCHAR2、NUMBER、DATE 等)的列或常量,不能出現 BLOB、CLOB 等類型的列。
  • 4、實際上分組列可以不出現在 SELECT 子句中,但這可能會讓查詢結果含義不明確。
  • 5、在 SELECT、HAVING、GROUP BY 子句中還可以出現某些特殊的函數,如 SYSDATE(感覺沒啥意義)。

1.2、WHERE 和 HAVING 的區別?

我首先要說明的是:“WHERE 和 HAVING 的區別?”這絕對是一個有問題的問題!因為它兩根本就沒有可比性,實際上問這種問題的人,往往 SQL 基礎也不夠扎實。

在包含 GROUP BY 子句的查詢語句中:WHERE 子句的作用是在對查詢結果分組前過濾行數據,將不符合條件的行去掉;而 HAVING 子句的作用是在對查詢結果分組後過濾組數據,將不符合條件的組去掉。換句話說,因為聚合函數的作用是提供有關組的信息,還沒分組之前當然是無法提供組的信息的,也就是說 WHERE 子句中無法使用聚合函數,這也正是會出現 HAVING 子句的原因。有了 HAVING 子句,我們就可以很方便的在分組之後對組的數據進行過濾了。一般來說,能用 WHERE 的過濾的就不應該用 HAVING 過濾!

2、GROUP BY 擴展分組

在實際業務開發中,只有標準分組可能還不夠,往往還需要更多維度的小計、合計。針對這類需求,Oracle 提供了豐富的擴展分組功能;儘管用 UNION ALL 一般也能實現類似效果,但不夠靈活且性能比較低。

2.1、ROLLUP 分組

ROLLUP 是 GROUP BY 子句的一種擴展,它允許計算標準分組及部分維度的小計、合計。語法:

GROUP BY ROLLUP(grouping_column_reference_list)

ROLLUP 的計算結果與括弧中指定列的順序有關,因為 ROLLUP 的分組過程具有方向性,先計算標準分組,然後從右到左遞減計算更高一級的小計,直到所有列被計算完,最後計算合計。當 ROLLUP 中指定 n 列時,整個計算過程中分組方式有 n+1 種。如GROUP BY ROLLUP(A,B)的分組過程相當於是:第 1 步按 GROUP BY(A,B) 分組求小計,第 2 步按 GROUP BY(A,NULL) 分組求小計,第 3 步按 GROUP BY(NULL,NULL) 分組求合計。

示例 1:

SELECT t.dept_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY ROLLUP(t.dept_code);

結果:

DEPT_CODE                                          SUM_SALARY MAX_SALARY
-------------------------------------------------- ---------- ----------
010102                                                  13500       7500
010103                                                   7850       5050
                                                        21350       7500

示例 2:

SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY ROLLUP(t.dept_code,t.post_code);

結果:

DEPT_CODE                                      POST_CODE                                    SUM_SALARY MAX_SALARY
---------------------------------------------- -------------------------------------------- ---------- ----------
010102                                         P40                                                7500       7500
010102                                         P50                                                6000       6000
010102                                                                                           13500       7500
010103                                         P40                                                5050       5050
010103                                         P50                                                2800       2800
010103                                                                                            7850       5050
                                                                                                 21350       7500

示例 3,部分 ROLLUP 分組(不需要某些小計、合計時可用該種寫法):

SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY t.dept_code,ROLLUP(t.post_code);

結果:

DEPT_CODE                                     POST_CODE                                    SUM_SALARY MAX_SALARY
--------------------------------------------- -------------------------------------------- ---------- ----------
010102                                        P40                                                7500       7500
010102                                        P50                                                6000       6000
010102                                                                                          13500       7500
010103                                        P40                                                5050       5050
010103                                        P50                                                2800       2800
010103                                                                                           7850       5050

2.2、CUBE 分組

CUBE 是 GROUP BY 子句的一種擴展,它允許計算標準分組及所有維度的小計、合計。語法:

GROUP BY CUBE(grouping_column_reference_list)

CUBE 會對所有可能的分組進行統計,從而生成交叉報表。CUBE 比 ROLLUP 的分組更多,且包含了 ROLLUP 的統計結果,且計算結果與分組列的順序無關,但如果列順序不同,預設的結果排序會有不同。當 CUBE 中指定 n 列時,整個計算過程中分組方式有 2 的 n 次方種。如GROUP BY CUBE(A,B)相當於:按 GROUP BY(A,B) 分組的小計,加按 GROUP BY(A,NULL) 分組的小計,加按 GROUP BY(NULL,B) 分組的小計,加按 GROUP BY(NULL,NULL) 分組的合計。

示例 1:

SELECT t.dept_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY CUBE(t.dept_code);

結果:

DEPT_CODE                                          SUM_SALARY MAX_SALARY
-------------------------------------------------- ---------- ----------
                                                        21350       7500
010102                                                  13500       7500
010103                                                   7850       5050

示例 2:

SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY CUBE(t.dept_code,t.post_code);

結果:

DEPT_CODE                                     POST_CODE                                  SUM_SALARY MAX_SALARY
--------------------------------------------- ------------------------------------------ ---------- ----------
                                                                                              21350       7500
                                              P40                                             12550       7500
                                              P50                                              8800       6000
010102                                                                                        13500       7500
010102                                        P40                                              7500       7500
010102                                        P50                                              6000       6000
010103                                                                                         7850       5050
010103                                        P40                                              5050       5050
010103                                        P50                                              2800       2800

示例 3,部分 CUBE 分組(不需要某些小計、合計時可用該種寫法):

SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY t.dept_code,CUBE(t.post_code);

結果:

DEPT_CODE                                    POST_CODE                                SUM_SALARY MAX_SALARY
-------------------------------------------- ---------------------------------------- ---------- ----------
010102                                                                                     13500       7500
010102                                       P40                                            7500       7500
010102                                       P50                                            6000       6000
010103                                                                                      7850       5050
010103                                       P40                                            5050       5050
010103                                       P50                                            2800       2800

2.3、GROUPING SETS 分組

GROUPING SETS 是 GROUP BY 子句的一種擴展,它允許一次計算多個標準分組的小計。語法:

GROUP BY GROUPING SETS(grouping_column_reference_list)

GROUPING SETS 的計算結果和分組列的順序無關,結果集排序也和分組列的順序無關。當 GROUPING SETS 中指定 n 列時,整個計算過程中分組方式有 n 種。如GROUPING SETS(A,B,C)相當於 GROUP BY A、GROUP BY B 和 GROUP BY C 這 3 個分組 UNION ALL 的結果。

示例 1:

SELECT t.dept_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY GROUPING SETS(t.dept_code);

結果:

DEPT_CODE                                          SUM_SALARY MAX_SALARY
-------------------------------------------------- ---------- ----------
010102                                                  13500       7500
010103                                                   7850       5050

示例 2:

SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY GROUPING SETS(t.dept_code,t.post_code);

結果:

DEPT_CODE                                     POST_CODE                                  SUM_SALARY MAX_SALARY
--------------------------------------------- ------------------------------------------ ---------- ----------
010102                                                                                        13500       7500
010103                                                                                         7850       5050
                                              P50                                              8800       6000
                                              P40                                             12550       7500

示例 3,部分 GROUPING SETS 分組:

SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY t.dept_code,GROUPING SETS(t.post_code);

結果:

DEPT_CODE                                    POST_CODE                                 SUM_SALARY MAX_SALARY
-------------------------------------------- ----------------------------------------- ---------- ----------
010103                                       P40                                             5050       5050
010102                                       P40                                             7500       7500
010102                                       P50                                             6000       6000
010103                                       P50                                             2800       2800

示例 4,GROUPING SETS 能夠接受 ROLLUP 和 CUBE 作為它的參數;GROUPING SETS 只對單列進行分組,而不提供合計的功能,如果需要 GROUPING SETS 提供合計,可用 ROLLUP 或 CUBE 作參數來提供合計功能(註意 ROLLUP 和 CUBE 不接受 GROUPING SETS 作參數,ROLLUP 和 CUBE 之間互相作為參數也不可以):

SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,MAX(t.post_salary) max_salary
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY GROUPING SETS(ROLLUP(t.dept_code),ROLLUP(t.post_code));

結果:

DEPT_CODE                                    POST_CODE                                  SUM_SALARY MAX_SALARY
-------------------------------------------- ------------------------------------------ ---------- ----------
010102                                                                                       13500       7500
010103                                                                                        7850       5050
                                             P50                                              8800       6000
                                             P40                                             12550       7500
                                                                                             21350       7500
                                                                                             21350       7500

2.4、複雜分組(組合列分組、重覆列分組、連接分組)

簡單來說:組合列分組就是允許 ROLLUP、CUBE 和 GROUPING SETS 中可以有多個列或列組合;重覆列分組就是允許 GROUP BY 後面重覆出現分組列;連接分組就是允許 GROUP BY 後面有多個 ROLLUP、CUBE 或 GROUPING SETS。

組合列分組有過濾某些小計或計算一些額外的小計的功能。前面的部分 ROLLUP 和 部分 CUBE 都沒有合計,使用組合列分組既可以實現部分 ROLLUP 或 部分 CUBE 的功能,還能有合計。如ROLLUP(A,(B,C)),既能過濾 B、C 的小計,還能計算 ABC 的合計。

連接分組的分組級別是由所有 ROLLUP、CUBE 或 GROUPING SETS 分組的級別組成的笛卡爾積。如ROLLUP(A,B),ROLLUP(C,D,E)的分組級別是 (2+1)×(3+1)=12,CUBE(A,B),CUBE(C,D,E)的分組級別是 (4)×(8)=32,CUBE(A,B),CUBE(C,D,E)的分組級別是 (4)×(8)=32。

按我的理解來說:複雜分組無非也就是對標準擴展分組的綜合運用。在實際開發中,可能會遇到一些僅使用標準擴展分組無法實現的需求,這時候就可以考慮靈活的運用標準擴展分組,通過複雜分組來實現。

3、GROUP BY 擴展函數

3.1、GROUPING 函數

GROUPING 語法:GROUPING(expr)。因為原始數據中可能存在 NULL,且小計或合計的值也可能為 NULL,這樣一來就顯得數據比較混亂了。當該函數出現在 SELECT 子句中時,如果聚集行的 expr 為 NULL,它就返回 1;如果常規行的 expr 為 NULL,它就返回 0。通常將一個分組列作為該函數的參數,然後通過判斷它的返回值來區分聚集行與常規行,從而進一步對結果集美化或過濾。示例:

SELECT DECODE(GROUPING(t.dept_code),1,'合計',t.dept_code) dept_code,
  SUM(t.post_salary) sum_salary,GROUPING(t.dept_code) gd
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY ROLLUP(t.dept_code);

結果:

DEPT_CODE                                          SUM_SALARY         GD
-------------------------------------------------- ---------- ----------
010102                                                  13500          0
010103                                                   7850          0
合計                                                    21350          1

3.2、GROUPING_ID 函數

GROUPING_ID 語法:GROUPING_ID(expr [, expr ]...)。當該函數出現在 SELECT 子句中時,它將返回與行相關聯的 GROUPING 位向量對應的數值。GROUPING_ID 函數按從左到右的順序計算,如果此列是分組列,則為 0,如果是小計或合計則為 1,然後按列的順序將計算結果組成二進位序列(位向量),最後將位向量轉化為十進位數。GROUPING_ID 函數在功能上等效於多個 GROUPING 函數的結果,有了 GROUPING_ID 就不必再寫多個 GROUPING 了,也使得行過濾條件更容易表達。當查詢結果有多個聚合級別時,該函數特別有用,可通過它的返回值來排序和過濾結果集。示例:

SELECT t.dept_code,t.post_code,SUM(t.post_salary) sum_salary,
  GROUPING_ID(t.dept_code) gd,
  GROUPING_ID(t.post_code) gp,
  GROUPING_ID(t.dept_code,t.post_code) gdp,
  GROUPING_ID(t.post_code,t.dept_code) gpd
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY CUBE(t.dept_code,t.post_code)
ORDER BY GROUPING_ID(t.dept_code,t.post_code);

結果(結果集相當於是按 GDP 升序排序):

DEPT_CODE                     POST_CODE                SUM_SALARY         GD         GP        GDP        GPD
----------------------------- ------------------------ ---------- ---------- ---------- ---------- ----------
010102                        P40                            7500          0          0          0          0
010103                        P40                            5050          0          0          0          0
010102                        P50                            6000          0          0          0          0
010103                        P50                            2800          0          0          0          0
010103                                                       7850          0          1          1          2
010102                                                      13500          0          1          1          2
                              P50                            8800          1          0          2          1
                              P40                           12550          1          0          2          1
                                                            21350          1          1          3          3

3.3、GROUP_ID 函數

GROUP_ID 語法:GROUP_ID()。當該函數出現在 SELECT 子句中時,如果結果集中存在 n 個重覆,那麼它將返回範圍從 0 到 n-1 中的數字,這對於從查詢結果中剔除重覆分組來說非常有用。示例(一般通過HAVING GROUP_ID()<1將重覆行全部剔除):

SELECT t.dept_code,SUM(t.post_salary) sum_salary,GROUP_ID() group_id
FROM demo.t_staff t
WHERE t.dept_code IN('010102','010103')
GROUP BY ROLLUP(t.dept_code),CUBE(t.dept_code);

結果:

DEPT_CODE                                          SUM_SALARY   GROUP_ID
-------------------------------------------------- ---------- ----------
010102                                                  13500          0
010103                                                   7850          0
010102                                                  13500          2
010103                                                   7850          2
010102                                                  13500          1
010103                                                   7850          1
                                                        21350          0

4、總結

本文主要講述了 Oracle 中分組查詢的標準分組、擴展分組、擴展函數等 GROUP BY 相關的知識點。

本文鏈接http://www.cnblogs.com/hanzongze/p/Oracle-Group-By.html
版權聲明:本文為博客園博主 韓宗澤 原創,作者保留署名權!歡迎通過轉載、演繹或其它傳播方式來使用本文,但必須在明顯位置給出作者署名和本文鏈接!本人初寫博客,水平有限,若有不當之處,敬請批評指正,謝謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在移動互聯網領域蓬勃發展的今天,APP的性能也成為各大公司重點關註的方向,該系列文章主要針對iOS的性能的幾個方面做一些研究。 什麼是HTTP2.0? 什麼是HTTP2.0? 網上很容易搜到關於HTTP2.0的概念的文章,這裡不再累述。 蘋果從iOS9開始支持HTTP2.0,對iOS開發人員來說,即 ...
  • 本文主要講述了在FineReport移動端中釘釘如何集成設計器的插件從而實現企業應用。 ...
  • phpMyAdmin的程式主要使用php和javascript開發,它的安裝使用都比較簡單而且已有很多相關介紹,今天來自騰訊雲資料庫團隊的高級開發工程師在此給大家介紹一下——源碼中的一個核心組件sql-parser ...
  • 3月9日14點,業內首個結合技術與應用的線上大數據技術峰會即將展開,屆時6位阿裡技術大咖與4位行業資深實踐者將從技術與業務兩個方面,與大家探討大數據如何最快落地到實踐。較為有看點的是: 最深入的實踐:本次線上峰會上,6個阿裡規模的大數據實踐將被深入分享,包括大數據平臺的性能調優、流式增量計算、持續發... ...
  • 前幾天卡Q在網上搜嘍大數據的課程,偶遇了廈門大學林子雨教授的大數據公開課,嚇得卡Q一身冷汗,為什麼呢? 這不,卡Q剛給自己的人生定了個小目標,比如,進入互聯網公司當個產品經理。因為在卡Q的眼裡,互聯網是個朝氣蓬勃的圈子,裡面的人都很牛掰,和聰明人共事,運氣總不會太差,所以,卡Q決定削尖了腦袋也要和聰... ...
  • 摘要: 目前我國物流業保持較快增長,但還是存在一些問題:物流成本高、效率低,條塊分割嚴重(自營物流、規模小、技術落後、標準不統一)、基礎設施相對滯後(物流基礎設施之間不銜接、不配套),對訂單創建到用戶簽收整套完整流程缺乏完善的監控和預警手段. ...
  • “令人驚喜的是,利用阿裡雲的數加平臺,我們差不多一個多月就搭建好了大數據平臺,並且可以通過圖形化的界面快速的開發,幾個開發人員很快的掌握,甚至我們把阿裡雲的開髮端給了業務部門,他們一些稍微資深一點的業務人員也可以使用,所以我們初步估計了一下,給我們節省的價值至少是千萬級的。 ...
  • 本教程為單機版+偽分散式的Hadoop,安裝過程寫的有些簡單,只作為筆記方便自己研究Hadoop用。 環境 Hadoop 有兩個主要版本,Hadoop 1.x.y 和 Hadoop 2.x.y 系列,比較老的教材上用的可能是 0.20 這樣的版本。Hadoop 2.x 版本在不斷更新,本教程均可適用 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...