1. 程式人生 > 其它 >SQL SERVER開窗函式

SQL SERVER開窗函式

SQL SERVER開窗函式 - csdbfans - 部落格園 (cnblogs.com)

 

先推薦一本書《程式設計師的SQL金典

今天將要介紹SQL Sever的開窗函式,何謂開窗函式,不懂吧。
反正對於我來說,我是摸不著頭腦了,第一次聽說過。那麼,什麼是開窗函式,其實可以理解為是聚合函式的一個加強版
因為使用聚合函式的話(不包括子查詢的情況),整個查詢都只能是聚合列返回值,而不能有基礎行的返回值。
那麼對於需要基礎行的返回值的話,就需要使用複雜的子查詢或者是儲存過程等才可以解決。
但是使用開窗函式,它能夠在同一行中同時返回基礎行的列和聚合列
在ISO SQL規定了這樣的函式為開窗函式,在Oracle中則被稱為分析函式,而在DB2中則被稱為OLAP函式

 

開窗函式與聚合函式一樣,都是對行的集合組進行聚合計算。
它用於為 "行" 定義一個視窗(這裡的視窗是指運算將要操作的行的集合),它對一組值進行操作,不需要使用GROUP BY子句對資料進行分組,能夠在同一行中同時返回基礎行的列和聚合列。
反正我理解這個函式 已經使用好子查詢或者是其它方式求得聚合列的值==>給我合併

以書中的例子一步一步來介紹,假設要計算所有人員的總數,我們可以執行下面的SQL語句:

SELECT COUNT(FName) FROM T_Person

這種方式比較直接,只返回一個聚合列的值,沒有任何基礎行的列的值。但是有時需要從不在聚合函式中的行的列中訪問這些聚合計算的值(即基礎行的列)。比如我們想查詢每個工資小於5000元的員工資訊(城市以及年齡),並且在每行中都顯示所有工資小於5000 元的員工個數,嘗試編寫下面的SQL語句:

SELECT FName, FCITY, FAGE, FSalary, COUNT(FName)
FROM T_Person
WHERE FSALARY<5000

執行上面的SQL以後我們會得到下面的錯誤資訊
選擇列表中的列'T_Person.FCity' 無效,因為該列沒有包含在聚合函式或GROUP BY 子句中。
這是因為所有不包含在聚合函式中的列必須宣告在GROUP BY 子句中,使用子查詢的方式是可以解決:

SELECT FName, FCITY, FAGE, FSalary,
(
SELECT COUNT(FName) FROM T_Person WHERE FSALARY<
5000 ) FROM T_Person WHERE FSALARY<5000

可以看到與聚合函式不同的是,開窗函式在聚合函式後增加了一個OVER關鍵字

格式為:函式名(列) OVER(選項) 

我這裡使用的是SQL Server 2008 R2,不知道從什麼時候開始,SQL SERVER也支援開窗函式中使用ORDER BY子句(注:書本中說MSSQLServer中是不支援開窗函式中使用ORDER BY子句)。
也正因為開窗函式支援了ORDER BY子句之後,開窗函式被分為兩大類。

聚合開窗函式====》聚合函式(列) OVER (選項),這裡的選項可以是PARTITION BY子句,但不可是ORDER BY子句
排序開窗函式====》排序函式(列) OVER(選項),這裡的選項可以是ORDER BY子句,也可以是 OVER(PARTITION BY子句 ORDER BY子句),但不可以是PARTITION BY子句

 聚合開窗函式

OVER 關鍵字表示把聚合函式當成聚合開窗函式而不是聚合函式。SQL 標準允許將所有聚合函式用做聚合開窗函式。
在上邊的例子中,
開窗函式COUNT(*) OVER()對於查詢結果的每一行都返回所有符合條件的行的條數。
OVER關鍵字後的括號中還經常新增選項用以改變進行聚合運算的視窗範圍。
如果OVER關鍵字後的括號中的選項為空,則開窗函式會對結果集中的所有行進行聚合運算

PARTITION BY 子句

開窗函式的OVER關鍵字後括號中的可以使用PARTITION BY子句來 "定義行的分割槽" 來供進行聚合計算。
與GROUP BY 子句不同,
PARTITION BY 子句建立的分割槽是獨立於結果集的,建立的分割槽只是供進行聚合計算的,
而且不同的開窗函式所建立的分割槽也不互相影響.

比如下面的SQL語句用於顯示每一個人員的資訊、所屬城市的人員數以及同齡人的人數:

SELECT FName,FCITY, FAGE, FSalary,
COUNT(FName) OVER(PARTITION BY FCITY),
COUNT(FName) OVER(PARTITION BY FAGE)
FROM T_Person

 排序開窗函式

對於排序開窗函式來講,它支援的開窗函式分別為:ROW_NUMBER(行號)、RANK(排名)、DENSE_RANK(密集排名)和NTILE(分組排名)

select  FName, FSalary, FCity, FAge,  
row_number() over(order by FSalary) as rownum,  
rank() over(order by FSalary) as rank,  
dense_rank() over(order by FSalary) as dense_rank,  
ntile(6) over(order by FSalary)as ntile 
from  T_Person 
order by  FName

下面的是執行結果: 

 

 

 

看到上面的結果了吧,下面來介紹下相關的內容。我們得到的最終結果是按照FName進行升序顯示的。

對於row_number() over(order by FSalary) as rownum來說,
這個排序開窗函式是按FSalary升序的方式來排序,並得出排序結果的序號
對於rank() over(order by FSalary) as rank來說,
這個排序形容函式是按FSalary升序的方式來排序,並得出排序結果的排名號。
這個函式求出來的排名結果可以排列,並列排名之後的排名將是並列的排名加上並列數
(簡單說每個人只有一種排名,然後出現兩個並列第一名的情況,
  這時候排在兩個第一名後面的人將是第三名,沒有了第二名,但是有兩個第一名)
對於dense_rank() over(order by FSalary) as dense_rank來說,
這個排序函式是按FSalary升序的方式來排序,並得出排序結果的排名號。
這個函式與rand()函式不同在於,
並列排名之後的排名只是並列排名加1
(簡單說每個人只有一種排名,然後出現兩個並列第一名的情況,
 排在兩個第一名後面的人將是第二名,也就是兩個第一名,一個第二名)
對於ntile(6) over(order by FSalary)as ntile 來說,
這個排序函式是按FSalary升序的方式來排序,並得出排序結果的分組數。

 排序函式和聚合開窗函式類似,也支援在OVER子句中使用PARTITION BY語句 

select  FName, FSalary, FCity, FAge,  
row_number() over(partition by FName  order by FSalary) as rownum,  
rank() over(partition by FName order by FSalary) as rank,  
dense_rank() over(partition by FName order by FSalary) as dense_rank,  
ntile(6) over(partition by FName order by FSalary)as ntile 
from  T_Person 
order by  FName

 關於PARTITION BY子句,請看上面的介紹,這裡就不再累贅了。

注意的是,在排序開窗函式中使用PARTITION BY子句需要放置在ORDER BY子句之前