SQL 聚合,開窗函數使用以及行轉列操作


     關於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 時,后面帶的列,一定要考慮清楚,多想想,這里坑比較多,下面時查詢結果,對比行專列前后

       

 

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM