詳解SQL Server 2005四種排名函數(shù)
下面通過具體的方案將用來討論和演示不同的函數(shù)和它們的子句。
十一位演講者在會議中發(fā)表演講,并且為他們的講話獲得范圍為 1 到 9 的分數(shù)。結(jié)果被總結(jié)并存儲在下面的 SpeakerStats 表中:
Code |
每個演講者都在該表中具有一個行,其中含有該演講者的名字、議題、平均得分、填寫評價的與會者相對于參加會議的與會者數(shù)量的百分比以及該演講者發(fā)表演講的次數(shù)。本節(jié)演示如何使用新的排序函數(shù)分析演講者統(tǒng)計數(shù)據(jù)以生成有用的信息。
1、ROW_NUMBER()函數(shù)
返回結(jié)果集分區(qū)內(nèi)行的序列號,每個分區(qū)的第一行從 1 開始。一般與OVER連用。
例如,假設(shè)您要返回所有演講者的 speaker、track 和 score,同時按照 score 降序向結(jié)果行分配從 1 開始的連續(xù)值。以下查詢通過使用 ROW_NUMBER 函數(shù)并指定 OVER (ORDER BY score DESC) 生成所需的結(jié)果:
Code |
得分最高的演講者獲得行號 1,得分最低的演講者獲得行號 11。ROW_NUMBER 總是按照請求的排序為不同的行生成不同的行號。請注意,如果在 OVER() 選項中指定的 ORDER BY 列表不唯一,則結(jié)果是不確定的。這意味著該查詢具有一個以上正確的結(jié)果;在該查詢的不同調(diào)用中,可能獲得不同的結(jié)果。例如,在我們的示例中,有三個不同的演講者獲得相同的最高得分 (9):Jessica、Ron 和 Suzanne。由于 SQL Server 必須為不同的演講者分配不同的行號,因此您應(yīng)當假設(shè)分別分配給 Jessica、Ron 和 Suzanne 的值 1、2 和 3 是按任意順序分配給這些演講者的。如果值 1、2 和 3 被分別分配給 Ron、Suzanne 和 Jessica,則結(jié)果應(yīng)該同樣正確。
如果您指定一個唯一的 ORDER BY 列表,則結(jié)果總是確定的。例如,假設(shè)在演講者之間出現(xiàn)得分相同的情況時,您希望使用最高的 pctfilledevals 值來分出先后。如果值仍然相同,則使用最高的 numsessions 值來分出先后。最后,如果值仍然相同,則使用最低詞典順序 speaker 名字來分出先后。由于 ORDER BY 列表 — score、pctfilledevals、numsessions 和 speaker — 是唯一的,因此結(jié)果是確定的:
Code
SELECT ROW_NUMBER() OVER(ORDER BY score DESC, pctfilledevals DESC, numsessions DESC, speaker) AS rownum, speaker, track, score, pctfilledevals, numsessions
FROM SpeakerStatsrownum speaker trackscore pctfilledevals numsessions
------ ---------- ---------- ----------- -------------- -----------
1 Ron Dev 9 30 3
2 Suzanne DB 9 30 3
3 JessicaDev 9 19 1
4 Michele Sys 8 31 4
5 Kathy Sys 8 27 2
6 Mike DB 8 20 3
7 Kevin DB 7 25 4
8 Brian Sys 7 22 3
9 RobertDev 6 28 2
10 Joe Dev 6 20 2
11 Dan Sys 3 22 4
本節(jié)所講到排序函數(shù)的重要好處之一是它們的效率。SQL Server 的優(yōu)化程序只需要掃描數(shù)據(jù)一次,以便計算值。它完成該工作的方法是:使用在排序列上放置的索引的有序掃描,或者,如果未創(chuàng)建適當?shù)乃饕?,則掃描數(shù)據(jù)一次并對其進行排序。
另一個好處是語法的簡單性。為了讓您感受一下通過使用在 SQL Server 的較低版本中采用的基于集的方法來計算排序值是多么困難和低效,請考慮下面的 SQL Server 2000 查詢,它返回與上一個查詢相同的結(jié)果:
Code |
該查詢顯然比 SQL Server 2005 查詢復(fù)雜得多。此外,對于 SpeakerStats 表中的每個基礎(chǔ)行,SQL Server 都必須掃描該表的另一個實例中的所有匹配行。對于基礎(chǔ)表中的每個行,平均大約需要掃描該表的一半(最少)行。SQL Server 2005 查詢的性能惡化是線性的,而 SQL Server 2000 查詢的性能惡化是指數(shù)性的。即使是在相當小的表中,性能差異也是顯著的。
行號的一個典型應(yīng)用是通過查詢結(jié)果分頁。給定頁大?。ㄒ孕袛?shù)為單位)和頁號,需要返回屬于給定頁的行。例如,假設(shè)您希望按照“score DESC, speaker”順序從 SpeakerStats 表中返回第二頁的行,并且假定頁大小為三行。下面的查詢首先按照指定的排序計算派生表 D 中的行數(shù),然后只篩選行號為 4 到 6 的行(它們屬于第二頁):
Code |
以下為結(jié)果集:
rownum speaker trackscore |
用更一般的術(shù)語表達就是,給定 @pagenum 變量中的頁號和 @pagesize 變量中的頁大小,以下查詢返回屬于預(yù)期頁的行:
Code |
上述方法對于您只對行的一個特定頁感興趣的特定請求而言已經(jīng)足夠了。但是,當用戶發(fā)出多個請求時,該方法就不能滿足需要了,因為該查詢的每個調(diào)用都需要您對表進行完整掃描,以便計算行號。當用戶可能反復(fù)請求不同的頁時,為了更有效地進行分頁,請首先用所有基礎(chǔ)表行(包括計算得到的行號)填充一個臨時表,并且對包含這些行號的列進行索引:
|
然后,對于所請求的每個頁,發(fā)出以下查詢:
DECLARE @pagenum AS INT, @pagesize AS INT |
只有屬于預(yù)期頁的行才會被掃描。
分段
可以在行組內(nèi)部獨立地計算排序值,而不是為作為一個組的所有表行計算排序值。為此,請使用 PARTITION BY 子句,并且指定一個表達式列表,以標識應(yīng)該為其獨立計算排序值的行組。例如,以下查詢按照“score DESC, speaker”順序單獨分配每個 track 內(nèi)部的行號:
Code |
以下為結(jié)果集:
trackpos speaker score |
在 PARTITION BY 子句中指定 track 列會使得為具有相同 track 的每個行組單獨計算行號。
#p#
2、RANK, DENSE_RANK
RANK 和 DENSE_RANK 函數(shù)非常類似于 ROW_NUMBER 函數(shù),因為它們也按照指定的排序提供排序值,而且可以根據(jù)需要在行組(分段)內(nèi)部提供。但是,與 ROW_NUMBER 不同的是,RANK 和 DENSE_RANK 向在排序列中具有相同值的行分配相同的排序。當 ORDER BY 列表不唯一,并且您不希望為在 ORDER BY 列表中具有相同值的行分配不同的排序時,RANK 和 DENSE_RANK 很有用。RANK 和 DENSE_RANK 的用途以及兩者之間的差異可以用示例進行最好的解釋。以下查詢按照 score DESC 順序計算不同演講者的行號、排序和緊密排序值:
Code
SELECT speaker, track, score,
ROW_NUMBER() OVER(ORDER BY score DESC) AS rownum,
RANK() OVER(ORDER BY score DESC) AS rnk,
DENSE_RANK() OVER(ORDER BY score DESC) AS drnk
FROM SpeakerStatsspeaker trackscore rownum rnk drnk
---------- ---------- ----------- ------ --- ----
Jessica Dev 9 1 1 1
Ron Dev 9 21 1
Suzanne DB 9 31 1
KathySys 8 44 2
Michele Sys 854 2
Mike DB 864 2
KevinDB 777 3
BrianSys 787 3
Joe Dev 699 4
Robert Dev 6 10 9 4
Dan Sys 311 11 5
正如前面討論的那樣,score 列不唯一,因此不同的演講者可能具有相同的得分。行號確實代表下降的 score 順序,但是具有相同得分的演講者仍然獲得不同的行號。但是請注意,在結(jié)果中,所有具有相同得分的演講者都獲得相同的排序和緊密排序值。換句話說,當 ORDER BY 列表不唯一時,ROW_NUMBER 是不確定的,而 RANK 和 DENSE_RANK 總是確定的。排序值和緊密排序值之間的差異在于,排序代表:具有較高得分的行號加 1,而緊密排序代表:具有明顯較高得分的行號加 1。從您迄今為止已經(jīng)了解的內(nèi)容中,您可以推導(dǎo)出當 ORDER BY 列表唯一時,ROW_NUMBER、RANK 和 DENSE_RANK 產(chǎn)生完全相同的值。
3、NTILE
NTILE 使您可以按照指定的順序,將查詢的結(jié)果行分散到指定數(shù)量的組 (tile) 中。每個行組都獲得不同的號碼:第一組為 1,第二組為 2,等等。您可以在函數(shù)名稱后面的括號中指定所請求的組號,在 OVER 選項的 ORDER BY 子句中指定所請求的排序。組中的行數(shù)被計算為 total_num_rows / num_groups。如果有余數(shù) n,則前面 n 個組獲得一個附加行。因此,可能不會所有組都獲得相等數(shù)量的行,但是組大小最大只可能相差一行。例如,以下查詢按照 score 降序?qū)⑷齻€組號分配給不同的 speaker 行:
Code |
以下為結(jié)果集:
speaker trackscore rownum tile |
在 SpeakerStats 表中有 11 位演講者。將 11 除以 3 得到組大小 3 和余數(shù) 2,這意味著前面 2 個組將獲得一個附加行(每個組中有 4 行),而第三個組則不會得到附加行(該組中有 3 行)。組號(tile 號)1 被分配給行 1 到 4,組號 2 被分配給行 5 到 8,組號 3 被分配給行 9 到 11。通過該信息可以生成直方圖,并且將項目均勻分布到每個梯級。在我們的示例中,第一個梯級表示具有最高得分的演講者,第二個梯級表示具有中等得分的演講者,第三個梯級表示具有最低得分的演講者??梢允褂?CASE 表達式為組號提供說明性的有意義的備選含義:
Code |
以下為結(jié)果集:
speaker trackscore scorecategory |
【編輯推薦】