關於SQL操作聚合函數,常用的如 Max、Sum、Avg、Count 等等,搭配着Group by 在不考慮性能的情況下,加上一些 inner, where 之類的基本可滿足大部分查詢要求。從最開始實習到工作一年的時候,查詢很少用過其他的函數,但隨着接手的業務需求逐漸變多變雜。在考慮到性能的情況下,僅僅掌握這些是不夠的,尤其面對某類業務的時,會充斥的大量的子查詢,連接查詢,不好維護,寫的也費勁。
--班級最高分 select MAX(TempScore) MaxScore,ClassName from TestASD group by ClassName --班級平均分 select AVG(CONVERT(float,tempScore)) AvgScore,className from TestASD group by ClassName --班級總分 select SUM(CONVERT(float,TempScore)) SumScore,ClassName from TestASD group by ClassName --班級人數 select COUNT(1) ClassCount,className from TestASD group by ClassName
1. 聚合函數的死結在於查詢結果往往會隨着 Group by 后面列的增加而發生變化。當需要列出多個列的時候,除聚合函數包含的列以外,均要寫在Group by 身后。如下SQL語句
--人數 班級 select COUNT(1) Num,ClassName from TestASD group by ClassName --人數 最高分 班級 select COUNT(1) Num,ClassName,MAX(TempScore) Score from TestASD group by ClassName --人數 最高分 學生 班級 --錯誤,數據錯亂 select COUNT(1) Num,ClassName,MAX(TempScore) Score,UserCode from TestASD group by ClassName,UserCode
針對以上sql語句,對應的查詢結果, 可以發現,當第三條sql 語句多增加了一個UserCode的字段時,查詢的結果就開始錯亂,並不是我們想要的結果,若要得到正確的結果,必然少不了 Inner 查詢,我首先想到的是 查詢班級,和最高分作為一個表,然后將這個表和第二條查詢語句通過班級和分數進行關聯,進而查詢出需要的結果。一邊查詢一邊進行調整。

2 .開窗函數則不受Group by 的束縛,可以針對當前行返回多筆數據。語句如下,結果如上。一個是分組獲取每個班級的班級人數,另外一個則很適合取每個分組的第一筆數據
--開窗函數 函數(列) over(列) --統計每組的數量和 Select COUNT(1) over(partition by ClassName) Num, ClassName, MAX(TempScore) over(partition by ClassName) Score,UserCode from dbo.TestASD --開窗函數 統計每組的數量(排序) Select ROW_NUMBER() over(partition by ManId ORDER BY ManId) Num,* from ManDetatil
PARTITION BY 函數是獨立於結果集創建自己的分區,以上sql語句使用了兩個開窗函數,分別是 count() over() 、max() over() ,其各自創建的分區互不影響,一般而言,聚合函數可用於開窗函數,其格式為: 聚合函數(列) + over(列),看到這里,突然有一股似曾相識的感覺,沒錯,其實經常使用的分頁函數,便是開窗函數
Select * from ( Select ROW_NUMBER() over(order by usercode desc) RowNum, * from TestASD )TT Where TT.RowNum between 11 and 20
說到分頁,這里順帶記錄相關的兩個點,
①. 當需要記錄總條數時,使用開窗函數 over() 后面不加列,則針對結果集所有行進行計算
②. 除了Row_Number() 可以排序以外,還有Rank(),Dense_Rank() 函數進行排序,當需要計算學生所在的班級排名,年級排名的時候用起來賊方便
Select COUNT(1) over() TolCount, ROW_NUMBER() over(order by Convert(float,TempScore) desc) RowNum, --按順序,1,2,3,4,5.... RANK() over(order by Convert(float,TempScore) desc) RanNum, --並列排名,之后排名自動延后 1,2,3,3,3,6,6,8 Dense_rank() over(order by Convert(float,TempScore) desc) DRanNum, --並列排名,之后排名繼續,不延后 1,2,3,3,3,4,4,5
* from TestASD
3. 行轉列,這樣的需求也挺多的,最常見的,如某表記錄了學生姓名,成績,班級,此時需要查詢每個班級 優秀(90+),良好(80+),及格(60+),不及格(60-) 的人數
With XX as( Select COUNT(1) over(PARTITION by ClassName) ClassCount,* from( Select distinct UserCode,UserName,ClassName, MAX(CONVERT(float,TempScore)) over(partition by Usercode) MaxScore from TestASD)T ) --SUM+Case 進行行轉列 Select ClassName, ClassCount 班級總數, SUM(case when MaxScore >=90 then 1 else 0 end) 優秀, SUM(case when MaxScore>=80 and MaxScore<90 then 1 else 0 end) 良好, SUM(case when MaxScore>=60 and MaxScore<80 then 1 else 0 end) 及格, SUM(case when MaxScore<60 then 1 else 0 end) 不及格 From XX group by ClassName,ClassCount
切記,使用Group by 時,后面帶的列,一定要考慮清楚,多想想,這里坑比較多,下面時查詢結果,對比行專列前后

以上這些,會用之后,查詢基本上不會像以前那么頭疼....
