1. Group By 語句簡介:express
Group By語句從英文的字面意義上理解就是「根據(by)必定的規則進行分組(Group)」。它的做用是經過必定的規則將一個數據集劃分紅若干個小的區域,而後針對若干個小區域進行數據處理。函數
P.S. 這裏真是體會到了一個好的命名的力量,Group By從字面是直接去理解是很是好理解的。恩,之後在命名的環節必定要加把勁:)。話題扯遠了。ui
2. Group By 的使用:spa
上面已經給出了對Group By語句的理解。基於這個理解和SQL Server 2000的聯機幫助,下面對Group By語句的各類典型使用進行依次列舉說明。排序
2.1 Group By [Expressions]:ci
這個恐怕是Group By語句最多見的用法了,Group By + [分組字段](能夠有多個)。在執行了這個操做之後,數據集將根據分組字段的值將一個數據集劃分紅各個不一樣的小組。好比有以下數據集,其中水果名稱(FruitName)和出產國家(ProductPlace)爲聯合主鍵:it
FruitName | ProductPlace | Price |
Apple | China | $1.1 |
Apple | Japan | $2.1 |
Apple | USA | $2.5 |
Orange | China | $0.8 |
Banana | China | $3.1 |
Peach | USA | $3.0 |
若是咱們想知道每一個國家有多少種水果,那麼咱們能夠經過以下SQL語句來完成:io
SELECT COUNT(*) AS 水果種類, ProductPlace AS 出產國
FROM T_TEST_FRUITINFO
GROUP BY ProductPlacetable
這個SQL語句就是使用了Group By + 分組字段的方式,那麼這句SQL語句就能夠解釋成「我按照出產國家(ProductPlace)將數據集進行分組,而後分別按照各個組來統計各自的記錄數量。」很好理解對吧。這裏值得注意的是結果集中有兩個返回字段,一個是ProductPlace(出產國), 一個是水果種類。若是咱們這裏水果種類不是用Count(*),而是相似以下寫法的話:function
SELECT FruitName, ProductPlace FROM T_TEST_FRUITINFO GROUP BY ProductPlace
那麼SQL在執行此語句的時候會報以下的相似錯誤:
選擇列表中的列 ’T_TEST_FRUITINFO.FruitName’ 無效,由於該列沒有包含在聚合函數或 GROUP BY 子句中。
這就是咱們須要注意的一點,若是在返回集字段中,這些字段要麼就要包含在Group By語句的後面,做爲分組的依據;要麼就要被包含在聚合函數中。咱們能夠將Group By操做想象成以下的一個過程,首先系統根據SELECT 語句獲得一個結果集,如最開始的那個水果、出產國家、單價的一個詳細表。而後根據分組字段,將具備相同分組字段的記錄歸併成了一條記錄。這個時候剩下的那些不存在於Group By語句後面做爲分組依據的字段就有可能出現多個值,可是目前一種分組狀況只有一條記錄,一個數據格是沒法放入多個數值的,因此這裏就須要經過必定的處理將這些多值的列轉化成單值,而後將其放在對應的數據格中,那麼完成這個步驟的就是聚合函數。這就是爲何這些函數叫聚合函數(aggregate functions)了。
2.2 Group By All [expressions] :
Group By All + 分組字段, 這個和前面提到的Group By [Expressions]的形式多了一個關鍵字ALL。這個關鍵字只有在使用了where語句的,且where條件篩選掉了一些組的狀況才能夠看出效果。在SQL Server 2000的聯機幫助中,對於Group By All是這樣進行描述的:
若是使用 ALL 關鍵字,那麼查詢結果將包括由 GROUP BY 子句產生的全部組,即便某些組沒有符合搜索條件的行。沒有 ALL 關鍵字,包含 GROUP BY 子句的 SELECT 語句將不顯示沒有符合條件的行的組。
其中有這麼一句話「若是使用ALL關鍵字,那麼查詢結果將包含由Group By子句產生的全部組…沒有ALL關鍵字,那麼不顯示不符合條件的行組。」這句話聽起來好像挺耳熟的,對了,好像和LEFT JOIN 和 RIGHT JOIN 有點像。其實這裏是類比LEFT JOIN來進行理解的。仍是基於以下這樣一個數據集:
FruitName | ProductPlace | Price |
Apple | China | $1.1 |
Apple | Japan | $2.1 |
Apple | USA | $2.5 |
Orange | China | $0.8 |
Banana | China | $3.1 |
Peach | USA | $3.0 |
首先咱們不使用帶ALL關鍵字的Group By語句:
SELECT COUNT(*) AS 水果種類, ProductPlace AS 出產國
FROM T_TEST_FRUITINFO
WHERE (ProductPlace <> ’Japan’)
GROUP BY ProductPlace
那麼在最後結果中因爲Japan不符合where語句,因此分組結果中將不會出現Japan。
如今咱們加入ALL關鍵字:
SELECT COUNT(*) AS 水果種類, ProductPlace AS 出產國
FROM T_TEST_FRUITINFO
WHERE (ProductPlace <> ’Japan’)
GROUP BY ALL ProductPlace
從新運行後,咱們能夠看到Japan的分組,可是對應的「水果種類」不會進行真正的統計,聚合函數會根據返回值的類型用默認值0或者NULL來代替聚合函數的返回值。
2.3 GROUP BY [Expressions] WITH CUBE | ROLLUP:
首先須要說明的是Group By All 語句是不能和CUBE 和 ROLLUP 關鍵字一塊兒使用的。
首先先說說CUBE關鍵字,如下是SQL Server 2000聯機幫助中的說明:
指定在結果集內不只包含由 GROUP BY 提供的正常行,還包含彙總行。在結果集內返回每一個可能的組和子組組合的 GROUP BY 彙總行。GROUP BY 彙總行在結果中顯示爲 NULL,但可用來表示全部值。使用 GROUPING 函數肯定結果集內的空值是不是 GROUP BY 彙總值。
結果集內的彙總行數取決於 GROUP BY 子句內包含的列數。GROUP BY 子句中的每一個操做數(列)綁定在分組 NULL 下,而且分組適用於全部其它操做數(列)。因爲 CUBE 返回每一個可能的組和子組組合,所以不論指定分組列時所使用的是什麼順序,行數都相同。
咱們一般的Group By語句是按照其後所跟的全部字段進行分組,而若是加入了CUBE關鍵字之後,那麼系統將根據全部字段進行分組的基礎上,還會經過對全部這些分組字段全部可能存在的組合造成的分組條件進行分組計算。因爲上面舉的例子過於簡單,這裏就再適合了,如今咱們的數據集將換一個場景,一個表中包含人員的基本信息:員工所在的部門編號(C_EMPLINFO_DEPTID)、員工性別(C_EMPLINFO_SEX)、員工姓名(C_EMPLINFO_NAME)等。那麼我如今想知道每一個部門各個性別的人數,那麼咱們能夠經過以下語句獲得:
SELECT C_EMPLINFO_DEPTID, C_EMPLINFO_SEX, COUNT(*) AS C_EMPLINFO_TOTALSTAFFNUM
FROM T_PERSONNEL_EMPLINFO
GROUP BY C_EMPLINFO_DEPTID, C_EMPLINFO_SEX
可是若是我如今但願知道:
1. 全部部門有多少人(這裏至關於就不進行分組了,由於這裏已經對員工的部門和性別沒有作任何限制了,可是這的確也是一種分組條件的組合方式);
2. 每種性別有多人(這裏其實是僅僅根據性別(C_EMPLINFO_SEX)進行分組);
3. 每一個部門有多少人(這裏僅僅是根據部門(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,請仔細看一下你就會發現實際上這些記錄就是完成了上面我所列舉的全部統計數據的展示。使用過SQL Server 2005或者RDLC的朋友們必定對於矩陣的小計和分組功能有印象吧,是否是均可以經過這個獲得答案。我想RDLC中對於分組和小計的計算就是經過Group By的CUBE和ROLLUP關鍵字來實現的。(我的意見,未證明)
CUBE關鍵字還有一個極爲類似的兄弟ROLLUP, 一樣咱們先從這英文入手,ROLL UP是「向上卷」的意思,若是說CUBE的組合是絕對自由的,那麼ROLLUP的組合就須要有點約束了。咱們先來看看SQL Server 2000的聯機中對ROLLUP關鍵字的定義:
指定在結果集內不只包含由 GROUP BY 提供的正常行,還包含彙總行。按層次結構順序,從組內的最低級別到最高級別彙總組。組的層次結構取決於指定分組列時所使用的順序。更改分組列的順序會影響在結果集內生成的行數。
那麼這個順序是什麼呢?對了就是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相比,返回的數據行數減小了很多。:),仔細看一下,除了正常的Group By語句後,數據中還包含了:
1. 部門員工數;(向上捲了一次,此次先去掉了員工性別的分組限制)
2. 全部部門員工數;(向上又捲了依次,此次去掉了員工所在部門的分組限制)。
在現實的應用中,對於報表的一些統計功能是頗有幫助的。
這裏還有一個問題須要補充說明一下,若是咱們使用ROLLUP或者CUBE關鍵字,那麼將產生一些小計的行,這些行中被剔除在分組因素以外的字段將會被設置爲NULL,那麼還存在一種狀況,好比在做爲分組依據的列表中存在可空的行,那麼NULL也會被做爲一個分組表示出來,因此這裏咱們就不能僅僅經過NULL來判斷是否是小計記錄了。下面的例子展現了這裏說獲得的狀況。仍是咱們前面提到的水果例子,如今咱們在每種商品後面增長一個「折扣列」(Discount),用於顯示對應商品的折扣,這個數值是可空的,也就是能夠經過NULL來表示沒有對應的折扣信息。數據集以下所示:
FruitName | ProductPlace | Price | Discount |
Apple | China | $1.1 | 0.8 |
Apple | Japan | $2.1 | 0.9 |
Apple | USA | $2.5 | 1.0 |
Orange | China | $0.8 | NULL |
Banana | China | $3.1 | NULL |
Peach | USA | $3.0 | NULL |
如今咱們要統計「各類折扣對應有多少種商品,並總計商品的總數。」,那麼咱們能夠經過以下的SQL語句來完成:
SELECT COUNT(*) AS ProductCount, Discount
FROM T_TEST_FRUITINFO
GROUP BY Discount WITH ROLLUP
好了,運行一下,你會發現數據都正常出來了,按照如上的數據集,結果以下所示:
ProductCount | Discount |
3 | NULL |
1 | 0.8 |
1 | 0.9 |
1 | 1.0 |
6 | NULL |
好了,各類折扣的商品數量都出來了,可是在顯示「沒有折扣商品」和「商品小計」的時候判斷上確存在問題,由於存在兩條Discount爲Null的記錄。是哪一條呢?經過分析數據咱們知道第一條數據(3, Null)應該對應沒有折扣商品的數量,而(6,Null)應該對應全部商品的數量。須要判斷這兩個具備不一樣意義的Null就須要引入一個聚合函數Grouping。如今咱們把語句修改一下,在返回值中使用Grouping函數增長一列返回值,SQL語句以下:
SELECT COUNT(*) AS ProductCount, Discount, GROUPING(Discount) AS Expr1
FROM T_TEST_FRUITINFO
GROUP BY Discount WITH ROLLUP
這個時候,咱們再看看運行的結果:
ProductCount | Discount | Expr1 |
3 | NULL | 0 |
1 | 0.8 | 0 |
1 | 0.9 | 0 |
1 | 1.0 | 0 |
6 | NULL | 1 |
對於根據指定字段Grouping中包含的字段進行小計的記錄,這裏會標記爲1,咱們就能夠經過這個標記值將小計記錄從判斷那些因爲ROLLUP或者CUBE關鍵字產生的行。Grouping(column_name)能夠帶一個參數,Grouping就會去判斷對應的字段值的NULL是不是由ROLLUP或者CUBE產生的特殊NULL值,若是是那麼就在由Grouping聚合函數產生的新列中將值設置爲1。注意Grouping只會檢查Column_name對應的NULL來決定是否將值設置爲1,而不是徹底由此列是不是由ROLLUP或者CUBE關鍵字自動添加來決定的。
2.2 Group By 和 Having, Where ,Order by語句的執行順序:
最後要說明一下的Group By, Having, Where, Order by幾個語句的執行順序。一個SQL語句每每會產生多個臨時視圖,那麼這些關鍵字的執行順序就很是重要了,由於你必須瞭解這個關鍵字是在對應視圖造成前的字段進行操做仍是對造成的臨時視圖進行操做,這個問題在使用了別名的視圖尤爲重要。以上列舉的關鍵字是按照以下順序進行執行的:Where, Group By, Having, Order by。首先where將最原始記錄中不知足條件的記錄刪除(因此應該在where語句中儘可能的將不符合條件的記錄篩選掉,這樣能夠減小分組的次數),而後經過Group By關鍵字後面指定的分組條件將篩選獲得的視圖進行分組,接着系統根據Having關鍵字後面指定的篩選條件,將分組視圖後不知足條件的記錄篩選掉,而後按照Order By語句對視圖進行排序,這樣最終的結果就產生了。在這四個關鍵字中,只有在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。