天天看點

SQL中幾個常用的排序函數

     最近使用視窗函數的頻率越來越高,這裡打算簡單介紹一下幾個排序的函數,做一個引子希望以後這方面的問題能夠更深入的了解,這裡先簡單介紹一下幾個簡單的排序函數及其相關子句,這裡先從什麼是排序開始吧。

排序函數是做什麼的?

    排序函數的作用是基于一個結果集傳回一個排序值。排序值就是一個數字,這個數字是典型的以1開始且自增長為1的行值。由ranking函數決定排序值可以使唯一的對于目前結果集,或者某些行資料有相同的排序值。在接下來我将研究不同的排序函數以及如何使用這些函數。

    RANK函數每個分區的排序都是從1開始。“partition”是一組有相同指定分區列值的資料行的集合。如果一個分區中有相同排序列的值(這個列指定在ORDER BY後面),然後相同排序列值的行将會配置設定給相同的排序值。有點繞口,為了更好的了解,如何使用,讓我們看下下面的文法:

這裡有幾個參數:

<partition_column>: 指定一個或者多個列名作為分區資料

<order by column>: 确定一個或者多個列然後用來對每個分區的輸出資料進行排序

PARTITION BY子句是一個可選項。如是不使用,資料将按照一個分區對所有資料進行排序。如果指定了PARTITION BY子句,則每個分區的資料集都各自進行從1開始的排序。

現在對RANK函數的文法和如何工作有了一定的了解,下面運作一對該函數的例子。需要說明一下我的例子的運作環境都是AdventureWorks2012 資料庫,可以從網絡上下載下傳這裡給出一個下載下傳位址http://msftdbprodsamples.codeplex.com/releases/view/93587。

下面是第一個使用RANK函數的例子:

Code1: 隻有RANK函數不分區

運作代碼後,結果集如下:

如上所示,按照RANK函數使結果集按照列RankingValue進行了排序。在例子中排序是基于列PostalCode。每一個唯一的PostalCode 得到一個不同的排序值。這裡PostalCode 為03054 有兩行資料,它們的排序值都是1,因為有兩個1,是以排序2就被跳過。其餘的排序繼續往下依次進行。

    由于RANK函數的分區子句沒有使用,那麼整個結果集被當做一個單一的分區。如果我打算按照獨立的StateProvinceID 進行分區,然後進行排序我可以做按照如下的例子來執行:

Code 2: 使用分區子句

運作代碼後的結果集:

    在輸出結果中分為了兩個分區,一個分區是StateProvinceID 是23的,而另一個是包含StateProvinceID 值為46的、注意每個分區都是從1開始進行排序的。

    當運作RANK函數時,由于有一個相同的PostalCode ,輸出結果會跳過一個排序值2,通過使用DENSE_RANK函數我能生成一個不省略改相同排序值的一個排序。該函數文法如下:

文法中唯一的不同就是函數名稱的改變。讓我們運作下面的代碼來研究下函數:

Code3: 使用 DENSE_RANK

結果集如下:

    根據結果集,可以看到PostalCode 03064 有相同的排序值,但是下一個PostalCode 的排序值為2而不是3了。與RANK函數的不同就是當有重複排序值時它能保證了排序序列中沒有省略排序。

該函數将資料集合劃分為不同的組。得到組的數量是根據指定的一個整數來确定的。下面就是NTILE 函數的文法:

Where:

<integer_expression>: 确定建立不同組的數量

<partition_column>:确定一個或者多個列用來進行分區資料

為了更好地了解,讓我們回顧幾個不同的例子。運作下面代碼:

Code4: 使用NTILE 函數查詢

運作結果如下:

     通過觀察結果集,能很容易發現有兩個不同的NTileValue 的列值,1和2。兩個不同的NTileValue 值被建立是因為這裡我查詢語句中指定了“NTILE(2)” 。這個括号内的值就是整數表達式,作用就是指定建立的組的數量。當看到結果集中有10行資料,前五行NTileValue 為1,後五行為2。不出所料整個結果集被平均分成了兩組。

     如果不能被平均配置設定到不同個組的時候,比如參數導緻有不能被整除的時候。當發生這種情況是那麼将不能被整除的行按序放到每一個組内,知道所有的剩餘行都被配置設定完畢。如下所示:

Code 5: NTile 查詢不能平均配置設定結果集

運作代碼如下:

   這裡直奔主題,10個結果行,參數為4需要分成4組,那麼10除以4 餘數為2。這意味着前兩組會多一行比後兩組。如上所示,在這個輸出結果中1和2組都有3行,然後NTileValue 為3和4的組隻有兩行。

   跟RANK函數一樣,我們也能使用partition 分區子句來建立分區下的NTILE 函數。當引入PARTITION BY 子句時,每個分區内部都從1開始進行NTILE排序。下面展示一下運作代碼:

Code 6: 使用分區子句後,使用NTile 查詢不平均分組

   通過結果集可以看到加入分區子句後對NTILE函數的影響。如果觀察輸出的NTileValue列值,可以發現排序從StateProvinceID  為46開始重新從1開始。這就是加入“PARTITION BY StateProvinceID”子句的作用,先分區在分組排序。

    當打算為輸出的行生成一個行号時,行号順序地自增長,步長為1.為了完成目标我們需要使用ROW_NUMBER 函數。

下面是使用ROW_NUMBER 的例子:

代碼如下:

Code  7: 使用ROW_NUMBER 函數

如果想對輸出的PostalCode進行排序,但是你打算先按照StateProvinceID進行分組,再排序。為了實作上述要求,我加入PARTITION BY子句,代碼如下:

Code 8: 使用PARTITION BY 子句和ROW_NUMBER 函數查詢

正如你看到的結果,通過添加分區子句,行數列RowNumber 每個不同的StateProvinceID 值都會從1重新開始排序。

    本篇講了多種不同的排序資料的方式,并且有一些方式要求配置設定一個序列化的數字。我先後展示了如何使用ROW_NUMBER, NTILE, RANK 和 DENSE_RANK函數,如何為每一行資料生成序列化的列值。希望能夠讓大家在使用時更友善,這裡也隻是展示了一部分視窗函數的使用。還有很多新的視窗函數希望跟大家一起讨論學習。這裡隻是做一個簡單介紹了。