1. Group By 語句簡介:
Group By語句從英文的字面意義上理解就是“根據(jù)(by)一定的規(guī)則進行分組(Group)”。它的作用是通過一定的規(guī)則將一個數(shù)據(jù)集劃分成若干個小的區(qū)域,然后針對若干個小區(qū)域進行數(shù)據(jù)處理。
P.S. 這里真是體會到了一個好的命名的力量,Group By從字面是直接去理解是非常好理解的。恩,以后在命名的環(huán)節(jié)一定要加把勁:)。話題扯遠了。
2. Group By 的使用:
上面已經(jīng)給出了對Group By語句的理解。基于這個理解和SQL Server 2000的聯(lián)機幫助,下面對Group By語句的各種典型使用進行依次列舉說明。
2.1 Group By [Expressions]:
這個恐怕是Group By語句最常見的用法了,Group By + [分組字段](可以有多個)。在執(zhí)行了這個操作以后,數(shù)據(jù)集將根據(jù)分組字段的值將一個數(shù)據(jù)集劃分成各個不同的小組。比如有如下數(shù)據(jù)集,其中水果名稱(FruitName)和出產(chǎn)國家(ProductPlace)為聯(lián)合主鍵:
FruitNameProductPlacePrice
AppleChina$1.1
AppleJapan$2.1
AppleUSA$2.5
OrangeChina$0.8
BananaChina$3.1
PeachUSA$3.0
如果我們想知道每個國家有多少種水果,那么我們可以通過如下SQL語句來完成:
SELECT COUNT(*) AS 水果種類, ProductPlace AS 出產(chǎn)國
FROM T_TEST_FRUITINFO
GROUP BY ProductPlace
這個SQL語句就是使用了Group By + 分組字段的方式,那么這句SQL語句就可以解釋成“我按照出產(chǎn)國家(ProductPlace)將數(shù)據(jù)集進行分組,然后分別按照各個組來統(tǒng)計各自的記錄數(shù)量?!焙芎美斫鈱Π?。這里值得注意的是結果集中有兩個返回字段,一個是ProductPlace(出產(chǎn)國), 一個是水果種類。如果我們這里水果種類不是用Count(*),而是類似如下寫法的話:
SELECT FruitName, ProductPlace FROM T_TEST_FRUITINFO GROUP BY ProductPlace
那么SQL在執(zhí)行此語句的時候會報如下的類似錯誤:
選擇列表中的列 ’T_TEST_FRUITINFO.FruitName’ 無效,因為該列沒有包含在聚合函數(shù)或 GROUP BY 子句中。
這就是我們需要注意的一點,如果在返回集字段中,這些字段要么就要包含在Group By語句的后面,作為分組的依據(jù);要么就要被包含在聚合函數(shù)中。我們可以將Group By操作想象成如下的一個過程,首先系統(tǒng)根據(jù)SELECT 語句得到一個結果集,如最開始的那個水果、出產(chǎn)國家、單價的一個詳細表。然后根據(jù)分組字段,將具有相同分組字段的記錄歸并成了一條記錄。這個時候剩下的那些不存在于Group By語句后面作為分組依據(jù)的字段就有可能出現(xiàn)多個值,但是目前一種分組情況只有一條記錄,一個數(shù)據(jù)格是無法放入多個數(shù)值的,所以這里就需要通過一定的處理將這些多值的列轉化成單值,然后將其放在對應的數(shù)據(jù)格中,那么完成這個步驟的就是聚合函數(shù)。這就是為什么這些函數(shù)叫聚合函數(shù)(aggregate functions)了。
2.2 Group By All [expressions] :
Group By All + 分組字段, 這個和前面提到的Group By [Expressions]的形式多了一個關鍵字ALL。這個關鍵字只有在使用了where語句的,且where條件篩選掉了一些組的情況才可以看出效果。在SQL Server 2000的聯(lián)機幫助中,對于Group By All是這樣進行描述的:
如果使用 ALL 關鍵字,那么查詢結果將包括由 GROUP BY 子句產(chǎn)生的所有組,即使某些組沒有符合搜索條件的行。沒有 ALL 關鍵字,包含 GROUP BY 子句的 SELECT 語句將不顯示沒有符合條件的行的組。
其中有這么一句話“如果使用ALL關鍵字,那么查詢結果將包含由Group By子句產(chǎn)生的所有組...沒有ALL關鍵字,那么不顯示不符合條件的行組。”這句話聽起來好像挺耳熟的,對了,好像和LEFT JOIN 和 RIGHT JOIN 有點像。其實這里是類比LEFT JOIN來進行理解的。還是基于如下這樣一個數(shù)據(jù)集:
FruitNameProductPlacePrice
AppleChina$1.1
AppleJapan$2.1
AppleUSA$2.5
OrangeChina$0.8
BananaChina$3.1
PeachUSA$3.0
首先我們不使用帶ALL關鍵字的Group By語句:
SELECT COUNT(*) AS 水果種類, ProductPlace AS 出產(chǎn)國
FROM T_TEST_FRUITINFO
WHERE (ProductPlace <> ’Japan’)
GROUP BY ProductPlace
那么在最后結果中由于Japan不符合where語句,所以分組結果中將不會出現(xiàn)Japan。
現(xiàn)在我們加入ALL關鍵字:
SELECT COUNT(*) AS 水果種類, ProductPlace AS 出產(chǎn)國
FROM T_TEST_FRUITINFO
WHERE (ProductPlace <> ’Japan’)
GROUP BY ALL ProductPlace
重新運行后,我們可以看到Japan的分組,但是對應的“水果種類”不會進行真正的統(tǒng)計,聚合函數(shù)會根據(jù)返回值的類型用默認值0或者NULL來代替聚合函數(shù)的返回值。
2.3 GROUP BY [Expressions] WITH CUBE | ROLLUP:
首先需要說明的是Group By All 語句是不能和CUBE 和 ROLLUP 關鍵字一起使用的。
首先先說說CUBE關鍵字,以下是SQL Server 2000聯(lián)機幫助中的說明:
指定在結果集內(nèi)不僅包含由 GROUP BY 提供的正常行,還包含匯總行。在結果集內(nèi)返回每個可能的組和子組組合的 GROUP BY 匯總行。GROUP BY 匯總行在結果中顯示為 NULL,但可用來表示所有值。使用 GROUPING 函數(shù)確定結果集內(nèi)的空值是否是 GROUP BY 匯總值。
結果集內(nèi)的匯總行數(shù)取決于 GROUP BY 子句內(nèi)包含的列數(shù)。GROUP BY 子句中的每個操作數(shù)(列)綁定在分組 NULL 下,并且分組適用于所有其它操作數(shù)(列)。由于 CUBE 返回每個可能的組和子組組合,因此不論指定分組列時所使用的是什么順序,行數(shù)都相同。
我們通常的Group By語句是按照其后所跟的所有字段進行分組,而如果加入了CUBE關鍵字以后,那么系統(tǒng)將根據(jù)所有字段進行分組的基礎上,還會通過對所有這些分組字段所有可能存在的組合形成的分組條件進行分組計算。由于上面舉的例子過于簡單,這里就再適合了,現(xiàn)在我們的數(shù)據(jù)集將換一個場景,一個表中包含人員的基本信息:員工所在的部門編號(C_EMPLINFO_DEPTID)、員工性別(C_EMPLINFO_SEX)、員工姓名(C_EMPLINFO_NAME)等。那么我現(xiàn)在想知道每個部門各個性別的人數(shù),那么我們可以通過如下語句得到:
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX
但是如果我現(xiàn)在希望知道:
1. 所有部門有多少人(這里相當于就不進行分組了,因為這里已經(jīng)對員工的部門和性別沒有做任何限制了,但是這的確也是一種分組條件的組合方式);
2. 每種性別有多人(這里實際上是僅僅根據(jù)性別(C_EMPLINFO_SEX)進行分組);
3. 每個部門有多少人(這里僅僅是根據(jù)部門(C_EMPLINFO_DEPTID)進行分組);那么我們就可以使用ROLLUP語句了。
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX WITH CUBE
那么這里你可以看到結果集中多出了很多行,而且結果集中的某一個字段或者多個字段、甚至全部的字段都為NULL,請仔細看一下你就會發(fā)現(xiàn)實際上這些記錄就是完成了上面我所列舉的所有統(tǒng)計數(shù)據(jù)的展現(xiàn)。使用過SQL Server 2005或者RDLC的朋友們一定對于矩陣的小計和分組功能有印象吧,是不是都可以通過這個得到答案。我想RDLC中對于分組和小計的計算就是通過Group By的CUBE和ROLLUP關鍵字來實現(xiàn)的。(個人意見,未證實)
CUBE關鍵字還有一個極為相似的兄弟ROLLUP, 同樣我們先從這英文入手,ROLL UP是“向上卷”的意思,如果說CUBE的組合是絕對自由的,那么ROLLUP的組合就需要有點約束了。我們先來看看SQL Server 2000的聯(lián)機中對ROLLUP關鍵字的定義:
指定在結果集內(nèi)不僅包含由 GROUP BY 提供的正常行,還包含匯總行。按層次結構順序,從組內(nèi)的最低級別到級別匯總組。組的層次結構取決于指定分組列時所使用的順序。更改分組列的順序會影響在結果集內(nèi)生成的行數(shù)。
那么這個順序是什么呢?對了就是Group By 后面字段的順序,排在靠近Group By的分組字段的級別高,然后是依次遞減。如:Group By Column1, Column2, Column3。那么分組級別從高到低的順序是:Column1 > Column2 > Column3。還是看我們前面的例子,SQL語句中我們僅僅將CUBE關鍵字替換成ROLLUP關鍵字,如:
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX WITH ROLLUP
和CUBE相比,返回的數(shù)據(jù)行數(shù)減少了不少。:),仔細看一下,除了正常的Group By語句后,數(shù)據(jù)中還包含了:
1. 部門員工數(shù);(向上卷了一次,這次先去掉了員工性別的分組限制)
2. 所有部門員工數(shù);(向上又卷了依次,這次去掉了員工所在部門的分組限制)。
在現(xiàn)實的應用中,對于報表的一些統(tǒng)計功能是很有幫助的。
這里還有一個問題需要補充說明一下,如果我們使用ROLLUP或者CUBE關鍵字,那么將產(chǎn)生一些小計的行,這些行中被剔除在分組因素之外的字段將會被設置為NULL,那么還存在一種情況,比如在作為分組依據(jù)的列表中存在可空的行,那么NULL也會被作為一個分組表示出來,所以這里我們就不能僅僅通過NULL來判斷是不是小計記錄了。下面的例子展示了這里說得到的情況。還是我們前面提到的水果例子,現(xiàn)在我們在每種商品后面增加一個“折扣列”(Discount),用于顯示對應商品的折扣,這個數(shù)值是可空的,也就是可以通過NULL來表示沒有對應的折扣信息。數(shù)據(jù)集如下所示:
FruitNameProductPlacePriceDiscount
AppleChina$1.10.8
AppleJapan$2.10.9
AppleUSA$2.51.0
OrangeChina$0.8NULL
BananaChina$3.1NULL
PeachUSA$3.0NULL
現(xiàn)在我們要統(tǒng)計“各種折扣對應有多少種商品,并總計商品的總數(shù)?!?,那么我們可以通過如下的SQL語句來完成:
SELECT COUNT(*) AS ProductCount, Discount
FROM T_TEST_FRUITINFO
GROUP BY Discount WITH ROLLUP
好了,運行一下,你會發(fā)現(xiàn)數(shù)據(jù)都正常出來了,按照如上的數(shù)據(jù)集,結果如下所示:
ProductCountDiscount
3NULL
10.8
10.9
11.0
6NULL
好了,各種折扣的商品數(shù)量都出來了,但是在顯示“沒有折扣商品”和“商品小計”的時候判斷上確存在問題,因為存在兩條Discount為Null的記錄。是哪一條呢?通過分析數(shù)據(jù)我們知道第一條數(shù)據(jù)(3, Null)應該對應沒有折扣商品的數(shù)量,而(6,Null)應該對應所有商品的數(shù)量。需要判斷這兩個具有不同意義的Null就需要引入一個聚合函數(shù)Grouping?,F(xiàn)在我們把語句修改一下,在返回值中使用Grouping函數(shù)增加一列返回值,SQL語句如下:
SELECT COUNT(*) AS ProductCount, Discount, GROUPING(Discount) AS Expr1
FROM T_TEST_FRUITINFO
GROUP BY Discount WITH ROLLUP
這個時候,我們再看看運行的結果:
ProductCountDiscountExpr1
3NULL0
10.80
10.90
11.00
6NULL1
對于根據(jù)指定字段Grouping中包含的字段進行小計的記錄,這里會標記為1,我們就可以通過這個標記值將小計記錄從判斷那些由于ROLLUP或者CUBE關鍵字產(chǎn)生的行。Grouping(column_name)可以帶一個參數(shù),Grouping就會去判斷對應的字段值的NULL是否是由ROLLUP或者CUBE產(chǎn)生的特殊NULL值,如果是那么就在由Grouping聚合函數(shù)產(chǎn)生的新列中將值設置為1。注意Grouping只會檢查Column_name對應的NULL來決定是否將值設置為1,而不是完全由此列是否是由ROLLUP或者CUBE關鍵字自動添加來決定的。
2.3 Group By 和 Having, Where ,Order by語句的執(zhí)行順序:
最后要說明一下的Group By, Having, Where, Order by幾個語句的執(zhí)行順序。一個SQL語句往往會產(chǎn)生多個臨時視圖,那么這些關鍵字的執(zhí)行順序就非常重要了,因為你必須了解這個關鍵字是在對應視圖形成前的字段進行操作還是對形成的臨時視圖進行操作,這個問題在使用了別名的視圖尤其重要。以上列舉的關鍵字是按照如下順序進行執(zhí)行的:Where, Group By, Having, Order by。首先where將最原始記錄中不滿足條件的記錄刪除(所以應該在where語句中盡量的將不符合條件的記錄篩選掉,這樣可以減少分組的次數(shù)),然后通過Group By關鍵字后面指定的分組條件將篩選得到的視圖進行分組,接著系統(tǒng)根據(jù)Having關鍵字后面指定的篩選條件,將分組視圖后不滿足條件的記錄篩選掉,然后按照Order By語句對視圖進行排序,這樣最終的結果就產(chǎn)生了。在這四個關鍵字中,只有在Order By語句中才可以使用最終視圖的列名,如:
SELECT FruitName, ProductPlace, Price, ID AS IDE, Discount
FROM T_TEST_FRUITINFO
WHERE (ProductPlace = N’china’)
ORDER BY IDE
這里只有在ORDER BY語句中才可以使用IDE,其他條件語句中如果需要引用列名則只能使用ID,而不能使用IDE。
Group By語句從英文的字面意義上理解就是“根據(jù)(by)一定的規(guī)則進行分組(Group)”。它的作用是通過一定的規(guī)則將一個數(shù)據(jù)集劃分成若干個小的區(qū)域,然后針對若干個小區(qū)域進行數(shù)據(jù)處理。
P.S. 這里真是體會到了一個好的命名的力量,Group By從字面是直接去理解是非常好理解的。恩,以后在命名的環(huán)節(jié)一定要加把勁:)。話題扯遠了。
2. Group By 的使用:
上面已經(jīng)給出了對Group By語句的理解。基于這個理解和SQL Server 2000的聯(lián)機幫助,下面對Group By語句的各種典型使用進行依次列舉說明。
2.1 Group By [Expressions]:
這個恐怕是Group By語句最常見的用法了,Group By + [分組字段](可以有多個)。在執(zhí)行了這個操作以后,數(shù)據(jù)集將根據(jù)分組字段的值將一個數(shù)據(jù)集劃分成各個不同的小組。比如有如下數(shù)據(jù)集,其中水果名稱(FruitName)和出產(chǎn)國家(ProductPlace)為聯(lián)合主鍵:
FruitNameProductPlacePrice
AppleChina$1.1
AppleJapan$2.1
AppleUSA$2.5
OrangeChina$0.8
BananaChina$3.1
PeachUSA$3.0
如果我們想知道每個國家有多少種水果,那么我們可以通過如下SQL語句來完成:
SELECT COUNT(*) AS 水果種類, ProductPlace AS 出產(chǎn)國
FROM T_TEST_FRUITINFO
GROUP BY ProductPlace
這個SQL語句就是使用了Group By + 分組字段的方式,那么這句SQL語句就可以解釋成“我按照出產(chǎn)國家(ProductPlace)將數(shù)據(jù)集進行分組,然后分別按照各個組來統(tǒng)計各自的記錄數(shù)量?!焙芎美斫鈱Π?。這里值得注意的是結果集中有兩個返回字段,一個是ProductPlace(出產(chǎn)國), 一個是水果種類。如果我們這里水果種類不是用Count(*),而是類似如下寫法的話:
SELECT FruitName, ProductPlace FROM T_TEST_FRUITINFO GROUP BY ProductPlace
那么SQL在執(zhí)行此語句的時候會報如下的類似錯誤:
選擇列表中的列 ’T_TEST_FRUITINFO.FruitName’ 無效,因為該列沒有包含在聚合函數(shù)或 GROUP BY 子句中。
這就是我們需要注意的一點,如果在返回集字段中,這些字段要么就要包含在Group By語句的后面,作為分組的依據(jù);要么就要被包含在聚合函數(shù)中。我們可以將Group By操作想象成如下的一個過程,首先系統(tǒng)根據(jù)SELECT 語句得到一個結果集,如最開始的那個水果、出產(chǎn)國家、單價的一個詳細表。然后根據(jù)分組字段,將具有相同分組字段的記錄歸并成了一條記錄。這個時候剩下的那些不存在于Group By語句后面作為分組依據(jù)的字段就有可能出現(xiàn)多個值,但是目前一種分組情況只有一條記錄,一個數(shù)據(jù)格是無法放入多個數(shù)值的,所以這里就需要通過一定的處理將這些多值的列轉化成單值,然后將其放在對應的數(shù)據(jù)格中,那么完成這個步驟的就是聚合函數(shù)。這就是為什么這些函數(shù)叫聚合函數(shù)(aggregate functions)了。
2.2 Group By All [expressions] :
Group By All + 分組字段, 這個和前面提到的Group By [Expressions]的形式多了一個關鍵字ALL。這個關鍵字只有在使用了where語句的,且where條件篩選掉了一些組的情況才可以看出效果。在SQL Server 2000的聯(lián)機幫助中,對于Group By All是這樣進行描述的:
如果使用 ALL 關鍵字,那么查詢結果將包括由 GROUP BY 子句產(chǎn)生的所有組,即使某些組沒有符合搜索條件的行。沒有 ALL 關鍵字,包含 GROUP BY 子句的 SELECT 語句將不顯示沒有符合條件的行的組。
其中有這么一句話“如果使用ALL關鍵字,那么查詢結果將包含由Group By子句產(chǎn)生的所有組...沒有ALL關鍵字,那么不顯示不符合條件的行組。”這句話聽起來好像挺耳熟的,對了,好像和LEFT JOIN 和 RIGHT JOIN 有點像。其實這里是類比LEFT JOIN來進行理解的。還是基于如下這樣一個數(shù)據(jù)集:
FruitNameProductPlacePrice
AppleChina$1.1
AppleJapan$2.1
AppleUSA$2.5
OrangeChina$0.8
BananaChina$3.1
PeachUSA$3.0
首先我們不使用帶ALL關鍵字的Group By語句:
SELECT COUNT(*) AS 水果種類, ProductPlace AS 出產(chǎn)國
FROM T_TEST_FRUITINFO
WHERE (ProductPlace <> ’Japan’)
GROUP BY ProductPlace
那么在最后結果中由于Japan不符合where語句,所以分組結果中將不會出現(xiàn)Japan。
現(xiàn)在我們加入ALL關鍵字:
SELECT COUNT(*) AS 水果種類, ProductPlace AS 出產(chǎn)國
FROM T_TEST_FRUITINFO
WHERE (ProductPlace <> ’Japan’)
GROUP BY ALL ProductPlace
重新運行后,我們可以看到Japan的分組,但是對應的“水果種類”不會進行真正的統(tǒng)計,聚合函數(shù)會根據(jù)返回值的類型用默認值0或者NULL來代替聚合函數(shù)的返回值。
2.3 GROUP BY [Expressions] WITH CUBE | ROLLUP:
首先需要說明的是Group By All 語句是不能和CUBE 和 ROLLUP 關鍵字一起使用的。
首先先說說CUBE關鍵字,以下是SQL Server 2000聯(lián)機幫助中的說明:
指定在結果集內(nèi)不僅包含由 GROUP BY 提供的正常行,還包含匯總行。在結果集內(nèi)返回每個可能的組和子組組合的 GROUP BY 匯總行。GROUP BY 匯總行在結果中顯示為 NULL,但可用來表示所有值。使用 GROUPING 函數(shù)確定結果集內(nèi)的空值是否是 GROUP BY 匯總值。
結果集內(nèi)的匯總行數(shù)取決于 GROUP BY 子句內(nèi)包含的列數(shù)。GROUP BY 子句中的每個操作數(shù)(列)綁定在分組 NULL 下,并且分組適用于所有其它操作數(shù)(列)。由于 CUBE 返回每個可能的組和子組組合,因此不論指定分組列時所使用的是什么順序,行數(shù)都相同。
我們通常的Group By語句是按照其后所跟的所有字段進行分組,而如果加入了CUBE關鍵字以后,那么系統(tǒng)將根據(jù)所有字段進行分組的基礎上,還會通過對所有這些分組字段所有可能存在的組合形成的分組條件進行分組計算。由于上面舉的例子過于簡單,這里就再適合了,現(xiàn)在我們的數(shù)據(jù)集將換一個場景,一個表中包含人員的基本信息:員工所在的部門編號(C_EMPLINFO_DEPTID)、員工性別(C_EMPLINFO_SEX)、員工姓名(C_EMPLINFO_NAME)等。那么我現(xiàn)在想知道每個部門各個性別的人數(shù),那么我們可以通過如下語句得到:
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX
但是如果我現(xiàn)在希望知道:
1. 所有部門有多少人(這里相當于就不進行分組了,因為這里已經(jīng)對員工的部門和性別沒有做任何限制了,但是這的確也是一種分組條件的組合方式);
2. 每種性別有多人(這里實際上是僅僅根據(jù)性別(C_EMPLINFO_SEX)進行分組);
3. 每個部門有多少人(這里僅僅是根據(jù)部門(C_EMPLINFO_DEPTID)進行分組);那么我們就可以使用ROLLUP語句了。
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX WITH CUBE
那么這里你可以看到結果集中多出了很多行,而且結果集中的某一個字段或者多個字段、甚至全部的字段都為NULL,請仔細看一下你就會發(fā)現(xiàn)實際上這些記錄就是完成了上面我所列舉的所有統(tǒng)計數(shù)據(jù)的展現(xiàn)。使用過SQL Server 2005或者RDLC的朋友們一定對于矩陣的小計和分組功能有印象吧,是不是都可以通過這個得到答案。我想RDLC中對于分組和小計的計算就是通過Group By的CUBE和ROLLUP關鍵字來實現(xiàn)的。(個人意見,未證實)
CUBE關鍵字還有一個極為相似的兄弟ROLLUP, 同樣我們先從這英文入手,ROLL UP是“向上卷”的意思,如果說CUBE的組合是絕對自由的,那么ROLLUP的組合就需要有點約束了。我們先來看看SQL Server 2000的聯(lián)機中對ROLLUP關鍵字的定義:
指定在結果集內(nèi)不僅包含由 GROUP BY 提供的正常行,還包含匯總行。按層次結構順序,從組內(nèi)的最低級別到級別匯總組。組的層次結構取決于指定分組列時所使用的順序。更改分組列的順序會影響在結果集內(nèi)生成的行數(shù)。
那么這個順序是什么呢?對了就是Group By 后面字段的順序,排在靠近Group By的分組字段的級別高,然后是依次遞減。如:Group By Column1, Column2, Column3。那么分組級別從高到低的順序是:Column1 > Column2 > Column3。還是看我們前面的例子,SQL語句中我們僅僅將CUBE關鍵字替換成ROLLUP關鍵字,如:
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX WITH ROLLUP
和CUBE相比,返回的數(shù)據(jù)行數(shù)減少了不少。:),仔細看一下,除了正常的Group By語句后,數(shù)據(jù)中還包含了:
1. 部門員工數(shù);(向上卷了一次,這次先去掉了員工性別的分組限制)
2. 所有部門員工數(shù);(向上又卷了依次,這次去掉了員工所在部門的分組限制)。
在現(xiàn)實的應用中,對于報表的一些統(tǒng)計功能是很有幫助的。
這里還有一個問題需要補充說明一下,如果我們使用ROLLUP或者CUBE關鍵字,那么將產(chǎn)生一些小計的行,這些行中被剔除在分組因素之外的字段將會被設置為NULL,那么還存在一種情況,比如在作為分組依據(jù)的列表中存在可空的行,那么NULL也會被作為一個分組表示出來,所以這里我們就不能僅僅通過NULL來判斷是不是小計記錄了。下面的例子展示了這里說得到的情況。還是我們前面提到的水果例子,現(xiàn)在我們在每種商品后面增加一個“折扣列”(Discount),用于顯示對應商品的折扣,這個數(shù)值是可空的,也就是可以通過NULL來表示沒有對應的折扣信息。數(shù)據(jù)集如下所示:
FruitNameProductPlacePriceDiscount
AppleChina$1.10.8
AppleJapan$2.10.9
AppleUSA$2.51.0
OrangeChina$0.8NULL
BananaChina$3.1NULL
PeachUSA$3.0NULL
現(xiàn)在我們要統(tǒng)計“各種折扣對應有多少種商品,并總計商品的總數(shù)?!?,那么我們可以通過如下的SQL語句來完成:
SELECT COUNT(*) AS ProductCount, Discount
FROM T_TEST_FRUITINFO
GROUP BY Discount WITH ROLLUP
好了,運行一下,你會發(fā)現(xiàn)數(shù)據(jù)都正常出來了,按照如上的數(shù)據(jù)集,結果如下所示:
ProductCountDiscount
3NULL
10.8
10.9
11.0
6NULL
好了,各種折扣的商品數(shù)量都出來了,但是在顯示“沒有折扣商品”和“商品小計”的時候判斷上確存在問題,因為存在兩條Discount為Null的記錄。是哪一條呢?通過分析數(shù)據(jù)我們知道第一條數(shù)據(jù)(3, Null)應該對應沒有折扣商品的數(shù)量,而(6,Null)應該對應所有商品的數(shù)量。需要判斷這兩個具有不同意義的Null就需要引入一個聚合函數(shù)Grouping?,F(xiàn)在我們把語句修改一下,在返回值中使用Grouping函數(shù)增加一列返回值,SQL語句如下:
SELECT COUNT(*) AS ProductCount, Discount, GROUPING(Discount) AS Expr1
FROM T_TEST_FRUITINFO
GROUP BY Discount WITH ROLLUP
這個時候,我們再看看運行的結果:
ProductCountDiscountExpr1
3NULL0
10.80
10.90
11.00
6NULL1
對于根據(jù)指定字段Grouping中包含的字段進行小計的記錄,這里會標記為1,我們就可以通過這個標記值將小計記錄從判斷那些由于ROLLUP或者CUBE關鍵字產(chǎn)生的行。Grouping(column_name)可以帶一個參數(shù),Grouping就會去判斷對應的字段值的NULL是否是由ROLLUP或者CUBE產(chǎn)生的特殊NULL值,如果是那么就在由Grouping聚合函數(shù)產(chǎn)生的新列中將值設置為1。注意Grouping只會檢查Column_name對應的NULL來決定是否將值設置為1,而不是完全由此列是否是由ROLLUP或者CUBE關鍵字自動添加來決定的。
2.3 Group By 和 Having, Where ,Order by語句的執(zhí)行順序:
最后要說明一下的Group By, Having, Where, Order by幾個語句的執(zhí)行順序。一個SQL語句往往會產(chǎn)生多個臨時視圖,那么這些關鍵字的執(zhí)行順序就非常重要了,因為你必須了解這個關鍵字是在對應視圖形成前的字段進行操作還是對形成的臨時視圖進行操作,這個問題在使用了別名的視圖尤其重要。以上列舉的關鍵字是按照如下順序進行執(zhí)行的:Where, Group By, Having, Order by。首先where將最原始記錄中不滿足條件的記錄刪除(所以應該在where語句中盡量的將不符合條件的記錄篩選掉,這樣可以減少分組的次數(shù)),然后通過Group By關鍵字后面指定的分組條件將篩選得到的視圖進行分組,接著系統(tǒng)根據(jù)Having關鍵字后面指定的篩選條件,將分組視圖后不滿足條件的記錄篩選掉,然后按照Order By語句對視圖進行排序,這樣最終的結果就產(chǎn)生了。在這四個關鍵字中,只有在Order By語句中才可以使用最終視圖的列名,如:
SELECT FruitName, ProductPlace, Price, ID AS IDE, Discount
FROM T_TEST_FRUITINFO
WHERE (ProductPlace = N’china’)
ORDER BY IDE
這里只有在ORDER BY語句中才可以使用IDE,其他條件語句中如果需要引用列名則只能使用ID,而不能使用IDE。