GROUP BY...HAVING 組合查詢大總結
環境:DB2 V9
說明:鑑於時間關係,再也不給出實驗環境腳本。
1、GROUP BY的理解
GROUP BY是SELECT語句的從句,用來指定查詢分組條件,主要用來對查詢的結果進行分組,相同組合的分組條件在結果集中只顯示一行記錄。使用GROUP BY從句時候,經過添加聚合函數(主要有COUNT()、SUM、MAX()、MIN()等)可使數據聚合。
GROUP BY插敘列中使用聚合函數是針對每一個分組的。例如:
SELECT SUBSTR(A.HYLB_DM,1,2),COUNT(*)
FROM DJ_ZT A
GROUP BY SUBSTR(A.HYLB_DM,1,2);
-----------------------
01 2071
02 679
03 17952
04 150
05 5921
06 11406
07 3030
08 51308
09 3940
10 1229
11 3548
12 6916
13 1003
14 537
15 11471
16 44
17 135
18 676
19 5747
'[Null]' 84
GROUP BY用來指定分組條件,是一個數學集合的概念,好比針對一列進行分組,則組合分組條件的集合數爲1。若是有兩個分組條件,則組合分組條件的集合數爲2。所以帶有GROUP BY的查詢通常成爲分組查詢,也叫組合查詢。組合記錄的多少取決於組合集合(不包含重複元素)中元素的個數。例如,組合條件爲一列,則查詢結果集記錄條數應該等於這個列全部字段所組成(數學意義上的)集合的元素個數(NULL字段也算一個)。若是有兩個組合列,則記錄數等於實際中存在的兩個列所組合的數目。
SELECT XZ,DFMC
FROM ODS.DM_RPT_QYHF
WHERE XZ='02'
GROUP BY XZ,DFMC;
--------------------------
02 '中外合做'
02 '中外合資'
02 '中外股份'
02 '外商投資'
02 '外國及港澳臺地區在境經營'
02 '外國及港澳臺地區常駐表明機構'
SELECT XZ,DFMC
FROM ODS.DM_RPT_QYHF
GROUP BY XZ,DFMC;
--------------------------
01 '其餘'
01 '內資公司'
01 '國有'
01 '股份合做'
01 '集體'
02 '中外合做'
02 '中外合資'
02 '中外股份'
02 '外商投資'
02 '外國及港澳臺地區在境經營'
02 '外國及港澳臺地區常駐表明機構'
03 '我的獨資'
03 '合夥企業'
03 '私營有限'
03 '私營股份'
'[Null]' '[Null]'
SELECT XZ,DFDM,DFMC,COUNT(*)
FROM ODS.DM_RPT_QYHF
GROUP BY XZ,DFDM,DFMC;
----------------------------------------------------
01 01 '國有' 4
01 02 '集體' 4
01 03 '股份合做' 3
01 04 '內資公司' 26
01 05 '其餘' 2
02 01 '中外合資' 2
02 02 '中外合做' 4
02 03 '外商投資' 28
02 04 '中外股份' 4
02 06 '外國及港澳臺地區在境經營' 1
02 07 '外國及港澳臺地區常駐表明機構' 1
03 01 '私營有限' 6
03 02 '私營股份' 4
03 03 '我的獨資' 1
03 04 '合夥企業' 1
'[Null]' '[Null]' '[Null]' 43
GROUP BY組合列必須出現查詢的SELECT關鍵字後面,相同組合條件的狀況下僅僅保留一個。所以,經過SELECT...GROUP BY查詢出的各個列都應該是數目相同,要達到相同的目的,有兩種途徑:一種是將要查詢的字段方到組合條件中,一種是在非組合條件的字段上使用聚合函數,固然也能夠在組合列上聚合函數。處子以外,別無它法!若是查詢的各個列結果數目不相等,則結果集會出現「不能對齊」的錯誤。 所以,將非組合條件的列在不使用聚合函數條件下放到要查詢的列中,這種作法是徹底錯誤的。
SELECT XZ
FROM ODS.DM_RPT_QYHF
GROUP BY XZ;
-----------------------
01
02
03
'[Null]'
GROUP BY在作組合查詢的時候,會對NULL的分組單獨造成一行,進行統計。參看上面的SQL。
GROUP BY對組合條件列來講,自己就會自動分組(剔除重複的列),所以在組合條件的列上應用DISTINCT關鍵字是多於的。可是用在非組合條件(都有聚合函數)的列上使用DISTINCT卻不是多餘的。
SELECT COUNT(DISTINCT(A.QYLX_ZL))
FROM DJ_ZT A
GROUP BY SUBSTR(A.QYLX_ZL,1,1);
-------------------------
1 3 11
2 2 9
3 6 2
4 5 10
5 3 9
6 1 6
7 2 2
8 2 1
9 3 2
GROUP BY不但能夠對列組合,還能夠對列的表達式進行組合。
例如:
SELECT
COUNT(A.BS) AS HS,
B.HYML_DM AS HYML_DM,
(SELECT HYML_MC FROM DM_HYML WHERE HYML_DM=B.HYML_DM) AS HYML_MC
FROM DJ_ZT A RIGHT OUTER JOIN DM_HYML B
ON SUBSTR(A.HYLB_DM,1,2)=B.HYML_DM
GROUP BY B.HYML_DM;
-----------------------------------
2071 01 '農、林、牧、漁業'
17952 03 '製造業'
679 02 '採礦業'
150 04 '電力、燃氣及水的生產和供應業'
5921 05 '建築業'
11406 06 '交通運輸、倉儲和郵政業'
3030 07 '信息傳輸、計算機服務和軟件業'
51308 08 '批發和零售業'
3940 09 '住宿和餐飲業'
1229 10 '金融業'
3548 11 '房地產業'
6916 12 '租賃和商務服務業'
1003 13 '科學研究、技術服務和地質勘查業'
537 14 '水利、環境和公共設施管理業'
11471 15 '居民服務和其餘服務業'
44 16 '教育'
135 17 '衛生、社會保障和社會福利業'
676 18 '文化、體育和娛樂業'
5747 19 '公共管理和社會組織'
0 20 '國際組織'
能夠在SELECT ... GROUP BY 分組後篩選數據。篩選的關鍵字是HAVING。HAVING的做用和WHERE相似。都是用來過濾查詢的中間記錄。可是,HAVING從句指定的每一個列規範必須出如今一個聚合函數內,或者出如今GROUP BY從句命名的列中。與WHERE不一樣的是:WHERE是在分組前(查詢後)篩選數據;HAVING是在分組後篩選數據。
例如:
SELECT
SUBSTR(A.HYLB_DM,1,2),
COUNT(*),
SUM(A.ZCZB)
FROM DJ_ZT A
GROUP BY SUBSTR(A.HYLB_DM,1,2)
HAVING MAX(YEAR(A.CJRQ))<>2007;
-----------------------------
08 51308 2988475.0376
SELECT
SUBSTR(A.HYLB_DM,1,2),
COUNT(*),
SUM(A.ZCZB)
FROM DJ_ZT A
GROUP BY SUBSTR(A.HYLB_DM,1,2)
HAVING MAX(YEAR(A.CJRQ))<>2007 AND COUNT(*)>2;
------------------------------
08 51308 2988475.0376
再如一個比較特殊的例子,對比一下看看:
SELECT SUBSTR(HY_DM,1,2), COUNT(HY_DM)
FROM DM_HY
GROUP BY SUBSTR(HY_DM,1,2)
ORDER BY SUBSTR(HY_DM,1,2);
------------------------------
01 53
02 44
03 620
04 14
05 15
06 58
07 21
08 117
09 10
10 21
11 6
12 37
13 30
14 26
15 21
16 18
17 22
18 38
19 34
20 2
SELECT SUBSTR(HY_DM,1,2), COUNT(HY_DM)
FROM DM_HY
GROUP BY SUBSTR(HY_DM,1,2)
HAVING COUNT(*)>100
ORDER BY SUBSTR(HY_DM,1,2);
------------------------------
03 620
08 117
今後能夠看出COUNT(*)是對每個分組的。
另外,有時候能夠在分組以前進行數據篩選並排序,好比:
SELECT SUBSTR(A.HY_DM,1,2)
FROM DM_HY A
WHERE SUBSTR(A.HY_DM,1,2) NOT LIKE '01'
GROUP BY SUBSTR(A.HY_DM,1,2)
ORDER BY SUBSTR(A.HY_DM,1,2) ASC;
---------------
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
2、GROUP BY的高級用法
一、GROUP BY ... WITH ROLLUP 上滾統計
SELECT SUBSTR(A.HYLB_DM,1,2),COUNT(*)
FROM DJ_ZT A
GROUP BY SUBSTR(A.HYLB_DM,1,2) WITH ROLLUP;
-------------------------------
'[Null]' 127847
01 2071
02 679
03 17952
04 150
05 5921
06 11406
07 3030
08 51308
09 3940
10 1229
11 3548
12 6916
13 1003
14 537
15 11471
16 44
17 135
18 676
19 5747
'[Null]' 84
二、GROUP BY ... WITH CUBE
這個查詢對於一個組合條件時候和上滾查詢的結果相同,但有多個組合條件時候,此語句會產生用NULL和各個組合字段進行匹配,造成新的記錄行,並進行統計。這個函數平時很不經常使用。
一個組合條件的狀況:
SELECT SUBSTR(A.HYLB_DM,1,2),COUNT(*)
FROM DJ_ZT A
WHERE A.ZCZB>100
GROUP BY SUBSTR(A.HYLB_DM,1,2) WITH CUBE;
-------------------------------
'[Null]' 11026
01 350
02 18
03 2721
04 47
05 1228
06 235
07 292
08 2477
09 212
10 135
11 1430
12 420
13 116
14 86
15 988
16 8
17 9
18 68
19 182
'[Null]' 4
兩個組合條件,對比一下,一目瞭然:
第一種:不加WITH CUBE條件:
SELECT SUBSTR(A.HYLB_DM,1,2),SUBSTR(A.QYLX_ZL,1,1),COUNT(*)
FROM DJ_ZT A
WHERE A.ZCZB>100
AND A.HYLB_DM IS NOT NULL
AND A.QYLX_ZL IS NOT NULL
AND SUBSTR(A.HYLB_DM,1,2) NOT IN('03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20')
AND A.QYLX_DM='01'
GROUP BY SUBSTR(A.HYLB_DM,1,2),SUBSTR(A.QYLX_ZL,1,1);
---------------------------
01 1 41
01 3 18
02 1 4
第二種:加上WITH CUBE條件:
SELECT SUBSTR(A.HYLB_DM,1,2),SUBSTR(A.QYLX_ZL,1,1),COUNT(*)
FROM DJ_ZT A
WHERE A.ZCZB>100
AND A.HYLB_DM IS NOT NULL
AND A.QYLX_ZL IS NOT NULL
AND SUBSTR(A.HYLB_DM,1,2) NOT IN('03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20')
AND A.QYLX_DM='01'
GROUP BY SUBSTR(A.HYLB_DM,1,2),SUBSTR(A.QYLX_ZL,1,1) WITH CUBE;
---------------------------
'[Null]' 1 45
'[Null]' 3 18
'[Null]' '[Null]' 63
01 '[Null]' 59
02 '[Null]' 4
01 1 41
01 3 18
02 1 4
3、核心原理
只有深刻理解這些語句執行的過程才能作到心中有數,明明白白寫SQL。下面是帶有WHERE和HAVING的SELECT語句執行過程:
一、執行WHERE篩選數據
二、執行GROUP BY分組造成中間分組表
三、執行WITH ROLLUP/CUBE生成統計分析數據記錄並加入中間分組表
四、執行HAVING篩選中間分組表
五、執行ORDER BY排序
呵呵,知道了執行過程,神祕的GROUP/WHERE/HAVING/WITH...將再也不神祕。