做爲一名開發人員來說,我感受在職場白混了好多年,多是本身真的沒有進取的精神吧,看了《程序員的SQL金典》這本電子書,真的讓我學到了很多知識,真心喜歡這本電子書,書中講解的內容比較好懂,也比較實用。謝謝做者的辛勤汗水:)。php
今天將要介紹SQL Sever的開窗函數,何謂開窗函數,不懂吧。反正對於我來講,我是摸不着頭腦了,第一次據說過。那麼,什麼是開窗函數,其實能夠理解爲是聚合函數的一個增強版。由於使用聚合函數的話(不包括子查詢的狀況),整個查詢都只能是聚合列返回值,而不能有基礎行的返回值。那麼對於須要基礎行的返回值的話,就須要使用複雜的子查詢或者是存儲過程等才能夠解決。可是使用開窗函數就能夠輕鬆解決,它可以在同一行中同時返回基礎行的列和聚合列。在ISO SQL規定了這樣的函數爲開窗函數,在Oracle中則被稱爲分析函數,而在DB2中則被稱爲OLAP函數。程序員
開窗函數與聚合函數同樣,都是對行的集合組進行聚合計算。它用於爲行定義一個窗口(這裏的窗口是指運算將要操做的行的集合),它對一組值進行操做,不須要使用GROUP BY子句對數據進行分組,可以在同一行中同時返回基礎行的列和聚合列。反正我理解這個函數已經使用好子查詢或者是其它方式求得聚合列的值給我合併。函數
以書中的例子一步一步來介紹,假設要計算全部人員的總數,咱們能夠執行下面的SQL語句:學習
SELECT COUNT(FName) FROM T_Person
這種方式比較直接,只返回一個聚合列的值,沒有任何基礎行的列的值。可是有時須要從不在聚合函數中的行的列中訪問這些聚合計算的值(即基礎行的列)。好比咱們想查詢每一個工資小於5000元的員工信息(城市以及年齡),而且在每行中都顯示全部工資小於5000 元的員工個數,嘗試編寫下面的SQL語句:spa
SELECT FName, FCITY, FAGE, FSalary, COUNT(FName) FROM T_Person WHERE FSALARY<5000
執行上面的SQL之後咱們會獲得下面的錯誤信息:
選擇列表中的列'T_Person.FCity' 無效,由於該列沒有包含在聚合函數或GROUP BY 子句中。
這是由於全部不包含在聚合函數中的列必須聲明在GROUP BY 子句中,使用子查詢的方式是能夠解決:code
SELECT FName, FCITY, FAGE, FSalary, ( SELECT COUNT(FName) FROM T_Person WHERE FSALARY<5000 ) FROM T_Person WHERE FSALARY<5000
雖然使用子查詢可以解決這個問題,可是子查詢的使用很是麻煩,使用開窗函數則能夠大大簡化實現,下面的SQL語句展現了若是使用開窗函數來實現一樣的效果:blog
SELECT FName, FCITY, FAGE, FSalary, COUNT(FName) OVER() 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子句get
第二大類:排序開窗函數====》排序函數(列) 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) FROM T_Person
OVER(PARTITION BY FCITY)表示對結果集按照FCITY進行分區,而且計算當前行所屬的組的聚合計算結果。在同一個SELECT語句中能夠同時使用多個開窗函數,並且這些開窗函數並不會相互干擾。好比下面的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(分組排名)。
先看一段SQL語句:
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子句以前。
至此本文完。