最近使用窗口函數的頻率愈來愈高,這裏打算簡單介紹一下幾個排序的函數,作一個引子但願之後這方面的問題可以更深刻的理解,這裏先簡單介紹一下幾個簡單的排序函數及其相關子句,這裏先從什麼是排序開始吧。sql
排序函數是作什麼的?數據庫
排序函數的做用是基於一個結果集返回一個排序值。排序值就是一個數字,這個數字是典型的以1開始且自增加爲1的行值。由ranking函數決定排序值可使惟一的對於當前結果集,或者某些行數據有相同的排序值。在接下來我將研究不一樣的排序函數以及如何使用這些函數。express
RANK函數每一個分區的排序都是從1開始。「partition」是一組有相同指定分區列值的數據行的集合。若是一個分區中有相同排序列的值(這個列指定在ORDER BY後面),而後相同排序列值的行將會分配給相同的排序值。有點繞口,爲了更好的理解,如何使用,讓咱們看下下面的語法:網絡
RANK ( ) OVER ( [ PARTITION BY <partition_column> ] ORDER BY <order_by_column> )
這裏有幾個參數:函數
PARTITION BY子句是一個可選項。如是不使用,數據將按照一個分區對全部數據進行排序。若是指定了PARTITION BY子句,則每一個分區的數據集都各自進行從1開始的排序。學習
如今對RANK函數的語法和如何工做有了必定的理解,下面運行一對該函數的例子。須要說明一下個人例子的運行環境都是AdventureWorks2012 數據庫,能夠從網絡上下載這裏給出一個下載地址http://msftdbprodsamples.codeplex.com/releases/view/93587。code
下面是第一個使用RANK函數的例子:blog
USE AdventureWorks2012; GO SELECT PostalCode, StateProvinceID, RANK() OVER (ORDER BY PostalCode ASC) AS RankingValue FROM Person.Address WHERE StateProvinceID IN (23,46);
Code1: 只有RANK函數不分區排序
運行代碼後,結果集以下:get
PostalCode StateProvinceID RankingValue --------------- --------------- -------------------- 03064 46 1 03064 46 1 03106 46 3 03276 46 4 03865 46 5 83301 23 6 83402 23 7 83501 23 8 83702 23 9 83864 23 10
如上所示,按照RANK函數使結果集按照列RankingValue進行了排序。在例子中排序是基於列PostalCode。每個惟一的PostalCode 獲得一個不一樣的排序值。這裏PostalCode 爲03054 有兩行數據,它們的排序值都是1,由於有兩個1,因此排序2就被跳過。其他的排序繼續往下依次進行。
因爲RANK函數的分區子句沒有使用,那麼整個結果集被當作一個單一的分區。若是我打算按照獨立的StateProvinceID 進行分區,而後進行排序我能夠作按照以下的例子來執行:
USE AdventureWorks2012; GO SELECT PostalCode, StateProvinceID, RANK() OVER (PARTITION BY StateProvinceID ORDER BY PostalCode ASC) AS RankingValue FROM Person.Address WHERE StateProvinceID IN (23,46);
Code 2: 使用分區子句
運行代碼後的結果集:
PostalCode StateProvinceID RankingValue --------------- --------------- -------------------- 83301 23 1 83402 23 2 83501 23 3 83702 23 4 83864 23 5 03064 46 1 03064 46 1 03106 46 3 03276 46 4 03865 46 5
在輸出結果中分爲了兩個分區,一個分區是StateProvinceID 是23的,而另外一個是包含StateProvinceID 值爲46的、注意每一個分區都是從1開始進行排序的。
當運行RANK函數時,因爲有一個相同的PostalCode ,輸出結果會跳過一個排序值2,經過使用DENSE_RANK函數我能生成一個不省略改相同排序值的一個排序。該函數語法以下:
DENSE_RANK ( ) OVER ( [ PARTIION BY <partition_column> ] ORDER BY <order_by_column> )
語法中惟一的不一樣就是函數名稱的改變。讓咱們運行下面的代碼來研究下函數:
USE AdventureWorks2012; GO SELECT PostalCode, StateProvinceID, DENSE_RANK() OVER (PARTITION BY StateProvinceID ORDER BY PostalCode ASC) AS RankingValue FROM Person.Address WHERE StateProvinceID IN (23,46);
Code3: 使用 DENSE_RANK
結果集以下:
PostalCode StateProvinceID RankingValue --------------- --------------- -------------------- 83301 23 1 83402 23 2 83501 23 3 83702 23 4 83864 23 5 03064 46 1 03064 46 1 03106 46 2 03276 46 3 03865 46 4
根據結果集,能夠看到PostalCode 03064 有相同的排序值,可是下一個PostalCode 的排序值爲2而不是3了。與RANK函數的不一樣就是當有重複排序值時它能保證了排序序列中沒有省略排序。
該函數將數據集合劃分爲不一樣的組。獲得組的數量是根據指定的一個整數來肯定的。下面就是NTILE 函數的語法:
NTILE (integer_expression) OVER ( [ PARTIION BY <partition_column> ] ORDER BY <order_by_column> )
Where:
爲了更好地理解,讓咱們回顧幾個不一樣的例子。運行下面代碼:
USE AdventureWorks2012; GO SELECT PostalCode, StateProvinceID, NTILE(2) OVER (ORDER BY PostalCode ASC) AS NTileValue FROM Person.Address WHERE StateProvinceID IN (23,46);
Code4: 使用NTILE 函數查詢
運行結果以下:
PostalCode StateProvinceID NTileValue --------------- --------------- -------------------- 03064 46 1 03064 46 1 03106 46 1 03276 46 1 03865 46 1 83301 23 2 83402 23 2 83501 23 2 83702 23 2 83864 23 2
經過觀察結果集,能很容易發現有兩個不一樣的NTileValue 的列值,1和2。兩個不一樣的NTileValue 值被建立是由於這裏我查詢語句中指定了「NTILE(2)」 。這個括號內的值就是整數表達式,做用就是指定建立的組的數量。當看到結果集中有10行數據,前五行NTileValue 爲1,後五行爲2。不出所料整個結果集被平均分紅了兩組。
若是不能被平均分配到不一樣個組的時候,好比參數致使有不能被整除的時候。當發生這種狀況是那麼將不能被整除的行按序放到每個組內,知道全部的剩餘行都被分配完畢。以下所示:
USE AdventureWorks2012; GO DECLARE @Integer_Expression int = 4; SELECT PostalCode, StateProvinceID, NTILE(@Integer_Expression) OVER (ORDER BY PostalCode ASC) AS NTileValue FROM Person.Address WHERE StateProvinceID IN (46,23);
Code 5: NTile 查詢不能平均分配結果集
運行代碼以下:
PostalCode StateProvinceID NTileValue --------------- --------------- -------------------- 03064 46 1 03064 46 1 03106 46 1 03276 46 2 03865 46 2 83301 23 2 83402 23 3 83501 23 3 83702 23 4 83864 23 4
這裏直奔主題,10個結果行,參數爲4須要分紅4組,那麼10除以4 餘數爲2。這意味着前兩組會多一行比後兩組。如上所示,在這個輸出結果中1和2組都有3行,而後NTileValue 爲3和4的組只有兩行。
跟RANK函數同樣,咱們也能使用partition 分區子句來建立分區下的NTILE 函數。當引入PARTITION BY 子句時,每一個分區內部都從1開始進行NTILE排序。下面展現一下運行代碼:
USE AdventureWorks2012; GO DECLARE @Integer_Expression int = 3; SELECT PostalCode, StateProvinceID, NTILE(@Integer_Expression) OVER (PARTITION BY StateProvinceID ORDER BY PostalCode ASC) AS NTileValue FROM Person.Address WHERE StateProvinceID IN (46,23);
Code 6: 使用分區子句後,使用NTile 查詢不平均分組
運行代碼以下:
PostalCode StateProvinceID NTileValue --------------- --------------- -------------------- 83301 23 1 83402 23 1 83501 23 2 83702 23 2 83864 23 3 03064 46 1 03064 46 1 03106 46 2 03276 46 2 03865 46 3
經過結果集能夠看到加入分區子句後對NTILE函數的影響。若是觀察輸出的NTileValue列值,能夠發現排序從StateProvinceID 爲46開始從新從1開始。這就是加入「PARTITION BY StateProvinceID」子句的做用,先分區在分組排序。
當打算爲輸出的行生成一個行號時,行號順序地自增加,步長爲1.爲了完成目標咱們須要使用ROW_NUMBER 函數。
下面是使用ROW_NUMBER 的例子:
ROW_NUMBER () OVER ( [ PARTIION BY <partition_expressions> ] ORDER BY <order_by_column> )
代碼以下:
USE AdventureWorks2012; GO SELECT PostalCode, StateProvinceID, ROW_NUMBER() OVER (ORDER BY PostalCode ASC) AS RowNumber FROM Person.Address WHERE StateProvinceID IN (46,23);
Code 7: 使用ROW_NUMBER 函數
運行結果以下:
PostalCode StateProvinceID RowNumber --------------- --------------- -------------------- 03064 46 1 03064 46 2 03106 46 3 03276 46 4 03865 46 5 83301 23 6 83402 23 7 83501 23 8 83702 23 9 83864 23 10
若是想對輸出的PostalCode進行排序,可是你打算先按照StateProvinceID進行分組,再排序。爲了實現上述要求,我加入PARTITION BY子句,代碼以下:
USE AdventureWorks2012; GO SELECT PostalCode, StateProvinceID, ROW_NUMBER() OVER (PARTITION BY StateProvinceID ORDER BY PostalCode ASC) AS RowNumber FROM Person.Address WHERE StateProvinceID IN (46,23);
Code 8: 使用PARTITION BY 子句和ROW_NUMBER 函數查詢
運行結果以下:
PostalCode StateProvinceID RowNumber --------------- --------------- -------------------- 83301 23 1 83402 23 2 83501 23 3 83702 23 4 83864 23 5 03064 46 1 03064 46 2 03106 46 3 03276 46 4
正如你看到的結果,經過添加分區子句,行數列RowNumber 每一個不一樣的StateProvinceID 值都會從1從新開始排序。
本篇講了多種不一樣的排序數據的方式,而且有一些方式要求分配一個序列化的數字。我前後展現瞭如何使用ROW_NUMBER, NTILE, RANK 和 DENSE_RANK函數,如何爲每一行數據生成序列化的列值。但願可以讓你們在使用時更方便,這裏也只是展現了一部分窗口函數的使用。還有不少新的窗口函數但願跟你們一塊兒討論學習。這裏只是作一個簡單介紹了。