[轉自]http://www.cnblogs.com/changbluesky/archive/2010/07/19/1780593.html
[由於本人是sql2000,不支持ROW_NUMBER所以並未進行測試,請在sql2005及以上版本使用.]
ROW_NUMBER 函數可以向查詢的結果行提供連續的整數值,通常配合OVER來使用:
() OVER(
[PARTITION BY ]
ORDER BY )
PARTITION BY是一個很實用的應用,可以在行組內部獨立地分段計算排序值,而不是爲作爲一個組的所有錶行計算排序值。
APPLY 運算符可以爲實現查詢操作的外部表表達式返回的每個行調用表值函數。表值函數作爲右輸入,外部表表達式作爲左輸入。通過對右輸入求值來獲得左輸入每一行的計算結果,生成的行被組合起來作爲最終輸出。APPLY 運算符生成的列的列表是左輸入中的列集,後跟右輸入返回的列的列表。要使用APPLY,數據庫兼容級別至少爲90(SQL Server 2005及其更高的版本)。
APPLY 有兩種形式:CROSS APPLY 和 OUTER APPLY。CROSS APPLY 僅返回外部表中通過表值函數生成結果集的行。OUTER APPLY 既返回生成結果集的行,也返回不生成結果集的行,其中表值函數生成的列中的值爲 NULL。
下面簡介一個簡單的應用(以NOTHWIND數據庫爲例):
1. 查詢出每個僱員最近銷售的兩筆訂單(table:Employees, Orders)
使用ROW_NUMBER來實現:按照EmployeeID做分段partition排序,把最近的兩個訂單排在最前面,編號分別爲1和2。外層查詢直接取出這編號小於等於2的就是最新的兩個訂單,CODE如下。
select ROW_NUMBER()over(partition by O.employeeID order by O.orderdate desc) as ROW,E.LastName,E.FirstName,O.*
from Employees E join Orders O on E.EmployeeID=O.EmployeeID
)A where A.ROW<=2
Order by A.EmployeeID
使用CROSS APPLY來實現:因爲APPLY是爲買個查詢操作的外部行返回值,所以每個行返回最新的兩個訂單即可,CODE如下。
CROSS APPLY (Select Top (2) * from Orders O where O.EmployeeID=E.EmployeeID order by O.OrderDate DESC) as OT
Order by E.EmployeeID
--注:APPLY中的查詢可以寫成一個函數,參數爲EmployeeID
2. 比較消耗的IO
用SET STATISTICS IO ON測試兩段查詢的IO使用情況:
使用ROW_NUMBER會產生一個臨時的工作表,對錶Order的邏輯查詢22次;
使用CROSS APPLY邏輯查詢627次,因爲對每一個EmployeeID,都會獨自執行一次APPLY中的查詢,找出兩個Order。
邏輯讀的次數:IO(ROW_NUMBER)<<IO(APPLY)
3. 比較消耗的TIME
對這兩個查詢來說,看起來APPLY消耗的時間更少。
消耗的時間:TIME(ROW_NUMBER)>TIME(APPLY)
4. 比較執行計劃
有不同的執行計劃:
ROW_NUMBER其中Clustered Index Scan,Hash Hatch, Sort花費最多。
APPLY其中Key Lookup花費最多。
從以上的比較可以看出,ROW_NUMBER會有較少的Logical Read/IO,但會花費較多的時間。兩種方法都能夠很好的處理TOP N之類的需求,都會有很好的處理效果,只是根據系統的loading來選擇合適的處理方法。
我個人比較喜歡用ROW_NUMBER。
另:由於測試環境的硬件配置及數據量的差異,測試結果會有所不同。