SQL查询入门(下篇)
引言
????? 在前兩篇文章中,對于單表查詢和多表查詢的概念做出了詳細的介紹,在本篇文章中會主要介紹聚合函數(shù)的使用和數(shù)據(jù)的分組.
?
簡介
????? 簡單的說,聚合函數(shù)是按照一定的規(guī)則將多行(Row)數(shù)據(jù)匯總成一行的函數(shù)。對數(shù)據(jù)進行匯總前,還可以按照特定的列(column)將數(shù)據(jù)進行分組(Group by)再匯總,然后按照再次給定的條件進行篩選(Having).
?
????? 聚合函數(shù)將多行數(shù)據(jù)進行匯總的概念可以簡單用下圖解釋:
???????
?
?
簡單聚合函數(shù)
?????? 簡單聚合函數(shù)是那些擁有很直觀將多行(Row)匯總為一行(Row)計算規(guī)則的函數(shù)。這些函數(shù)往往從函數(shù)名本身就可以猜測出函數(shù)的作用,而這些函數(shù)的參數(shù)都是數(shù)字類型的。簡單聚合函數(shù)包括:Avg,Sum,Max,Min.
?????? 簡單聚合函數(shù)的參數(shù)只能是數(shù)字類型,在SQL中,數(shù)字類型具體包括:tinyint,smallint,int,bigint,decimal,money,smallmoney,float,real.
?????? 在介紹簡單聚合函數(shù)之前,先來介紹一下Count()這個聚合函數(shù).
?
??????Count()
?????? Count函數(shù)用于計算給定條件下所含有的行(Row)數(shù).例如最簡單的:
????????
?????? 上表中,我想知道公司員工的個數(shù),可以簡單的使用:
SELECT COUNT(*) AS EmployeeNumber FROM HumanResources.Employee????
??????? 結(jié)果如下
????????
???????? 當Count()作用于某一特定列(Column),和以“*”作為參數(shù)時的區(qū)別是當Count(列名)碰到“Null”值時不會將其計算在內(nèi),例如:
????????我想知道公司中有上級的員工個數(shù):
SELECT COUNT(ManagerID) AS EmployeeWithSuperior FROM HumanResources.Employee???????
?
???????
?????? 可以看到,除了沒有上級的CEO之外,所有其他的員工已經(jīng)被統(tǒng)計在內(nèi).
?????????
?????? 也可以在Count()內(nèi)使用Distinct關(guān)鍵字來讓,每一列(Column)的每個相同的值只有一個被統(tǒng)計在內(nèi),比如:
???????我想統(tǒng)計公司中經(jīng)理層級的數(shù)量:
SELECT COUNT(DISTINCT ManagerID) AS NumberOfManager FROM HumanResources.Employee ???????????????? 結(jié)果如上.
?
??????? Avg(),Sum(),Max()和Min()
??????? 這幾個聚合函數(shù)除了功能不同以外,參數(shù)和用法幾乎相同。所以這里只對Avg()這個聚合函數(shù)進行解釋:
??????? Avg()表示計算在選擇范圍內(nèi)的匯總數(shù)據(jù)的平均值.這個過程中“Null”值不會被統(tǒng)計在內(nèi) ,例如:
????????我想獲得平均每位員工休假的時長:
SELECT AVG(VacationHours) AS 'Average vacation hours' FROM HumanResources.Employee??????? 結(jié)果如下:
??????
????? 因為默認用聚合函數(shù)進行數(shù)據(jù)匯總時,不包含null,但如果我想要包含null值,并在當前查詢中將Null值以其他值替代并參與匯總運算時,使用IsNull(column,value)
????? 例如:
????? 我想獲得平均每位員工的休假時長,如果員工沒有休假,則按休假10個小時計算
SELECT AVG(ISNULL(VacationHours, 10)) AS 'Average vacation hours' FROM HumanResources.Employee????? 結(jié)果如下:
??????
???? 也可以使用DISTINCT關(guān)鍵字在簡單聚合函數(shù)中讓每一個值唯一參與聚合匯總運算.在上面的Count函數(shù)中已經(jīng)解釋,這里不做重復(fù)。
???? 而關(guān)于Sum(),Max(),Min()等這些簡單聚合函數(shù),使用方法基本相同,這里就不重復(fù)了
?
????
將聚合函數(shù)得到的值按照列(Column)進行分組
????? 如果聚合函數(shù)所得到的結(jié)果無法按照特定的值進行分組,那聚合函數(shù)的作用就沒那么強了。在SQL中,使用Group by對聚合函數(shù)匯總的值進行分組。分組的概念可以下面這個簡單的例子表示:
?????? 例如:
???????我想根據(jù)不同省得到銷售人員所銷售的總和:
SELECT TerritoryID, SUM(SalesLastYear) AS ToTalSales FROM Sales.SalesPerson GROUP BY TerritoryID??????概念如下圖所示:
???????
?????
?? 跟在Group by后面的列名是分組的依據(jù)。當然在某些情況下,會有依據(jù)多個列(Column)進行分組的情況.下面這個例子有點實際意義:
???我想按照不同性別獲得不同經(jīng)理手下的員工的病假時間總和:
?
SELECT ManagerID, Gender, SUM(SickLeaveHours) AS SickLeaveHours, COUNT(*) AS EmployeeNumber FROM HumanResources.Employee GROUP BY Gender, ManagerID??
?? 結(jié)果如下:
?????
???? Group By后面多列,我們可以在邏輯思維上這么想,先根據(jù)每一列唯一的ManagerId和唯一的Gender進行Cross Join(如果你不懂什么Cross join,請看我前面的文章)得到唯一可以確定其他鍵(Key)的鍵,最后過濾掉聚合函數(shù)中不能返回值的行(Row)(也就是為Null)的行。再根據(jù)這實際上兩列,但邏輯上是一列的值作為分組依據(jù)。
???? 上圖中可以看到,我們首先按照經(jīng)理ID,進行分組,然后根據(jù)不同經(jīng)理手下的員工的性別,再次進行分總,最終按照這個分組條件得到病假時間總和.
???? 這里要注意,當使用Group By按照多列(Column)進行分組時,一定要注意出現(xiàn)在Group By后面的次序
???? 上面先出現(xiàn)Gender是先遍歷Gender的所有可能的值,再根據(jù)每個Gender可能的值去計算匹配ManagerID,最后再根據(jù)ManagerID來進行聚合函數(shù)運算,如果將上面Group By后面得列(Column)順序改為先ManagerId,再Gender,則意味著先遍歷ManagerID所有可能出現(xiàn)的值,再去匹配Gender,則結(jié)果如下:
?????
???? 從Gender(性別)變?yōu)镸(男性)開始,第二次遍歷ManagerId進行匹配:
??????
?
???? 從上面我們可以看出,雖然Group by后面出現(xiàn)列(Column)的次序不同,所得到結(jié)果的順序也不同,但所得到的數(shù)據(jù)集(DataSet)是完全一樣,所以,可以通過Order By子句將按照不同列次序進行Group By的查詢語句獲得相同的結(jié)果。這里就不再截圖了。
?
對分組完成后的數(shù)據(jù)集進行再次篩選(Having)?
????? 當對使用聚合函數(shù)進行分組后,可以再次使用放在Group By子句后的Having子句對分組后的數(shù)據(jù)進行再次的過濾.Having子句在某些方面很像Where子句,具體having表達式的使用可以看我前面文章中對where的講解。Having子句可以理解成在分組后進行二次過濾的語句.
????? 使用having子句非常簡單,但需要注意的是,having子句后面不能跟在select語句中出現(xiàn)的別名,而必須將Select語句內(nèi)的表達式再寫一遍,例如還是針對上面的表:
??????我想按照不同性別獲得不同經(jīng)理手下的員工的病假時間總和,這些經(jīng)理手下的員工需要大于2個人:
SELECT ManagerID, Gender, SUM(SickLeaveHours) AS SickLeaveHours, COUNT(*) AS EmployeeNumber FROM HumanResources.Employee GROUP BY ManagerID, Gender HAVING (EmployeeNumber > 2)??????注意,上面這句話是錯誤的,在Having子句后面不能引用別名或者變量名,如果需要實現(xiàn)上面那個效果,需要將Count(*)這個表達式再Having子句中重寫一遍,正確寫法如下:
SELECT ManagerID, Gender, SUM(SickLeaveHours) AS SickLeaveHours, COUNT(*) AS EmployeeNumber FROM HumanResources.Employee GROUP BY ManagerID, Gender HAVING (COUNT(*) > 2)?????? 結(jié)果如下:
??????
????? 我們看到,只有員工數(shù)大于2人的條件被選中。
?
????? 當然,Having子句最強大的地方莫過于其可以使用聚合函數(shù)作為表達式,這是在Where子句中不允許的。下面這個例子很好的演示了Having子句的強大之處:
????? 還是上面那個例子的數(shù)據(jù):
??????我想獲得不同經(jīng)理手下的員工的病假時間總和,并且這個經(jīng)理手下病假最多的員工的請假小時數(shù)大于病假最少員工的兩倍:
SELECT ManagerID, SUM(SickLeaveHours) AS TotalSickLeaveHours, COUNT(*) AS EmployeeNumber FROM HumanResources.Employee GROUP BY ManagerID HAVING (MAX(SickLeaveHours) > 2 * MIN(SickLeaveHours))???
???? 結(jié)果如下:
?????
?
???? 這里可以看出,Having子句實現(xiàn)如此簡單就能實現(xiàn)的強大功能,如果用where將會非常非常的麻煩。上面那個結(jié)果中,having語句聚合函數(shù)的作用范圍可以用下圖很好的演示出來:
?????
???? 上面可以看出被篩選后的數(shù)據(jù)滿足請假最多員工的小時數(shù)明顯大于請假最少員工小時數(shù)的兩倍。
?
小結(jié)
????? 本文以聚合函數(shù)概念為開始,講述了聚合函數(shù)使用中經(jīng)常用到的查詢,分組,過濾的概念和使用方式。使用好聚合函數(shù)可以將很多放到應(yīng)用程序業(yè)務(wù)層的任務(wù)轉(zhuǎn)到數(shù)據(jù)庫里來.這會對維護和性能提升很很大的幫助.
?
PS:SQL查詢?nèi)腴T就寫完了,雖然文章講的內(nèi)容很淺,但我在寫作的過程中腦中很多模糊的概念變得逐漸清晰。從而對自己也是一次再學習。我也努力將每一個概念以簡單的方式表現(xiàn)出來。技術(shù)文章就應(yīng)該這樣吧:-) 后續(xù)文章準備中ing….
總結(jié)
以上是生活随笔為你收集整理的SQL查询入门(下篇)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL查询入门(中篇)
- 下一篇: T-SQL查询进阶--基于列的逻辑表达式