2014-12-05 36 views
1

在這種情況下,CTE(微型優化,我知道...)中必需的SELECT TOP。SQL - 使用CTE並有效地選擇特定row_number處的行()

DECLARE @pageSize INT, @currentPage INT, @top INT 
SET @pageSize = 10 
SET @currentPage = 150 
SET @top = @pageSize * @currentPage + @pageSize 

WITH t AS 
(
    SELECT TOP(***@top***) ID, name 
    ROW_NUMBER() OVER (ORDER BY ID) AS _row, 
    FROM dbo.User 
) 
SELECT TOP(@pageSize) * 
FROM t 
WHERE t._row > (@[email protected]) 
ORDER BY t.ID 

以上以特定順序從行號列中返回10個(@pageSize)行的起始數字(@ top- @ pageSize)。 CTE聲明是否知道在CTE之外的「SELECT TOP」和WHERE子句(也在CTE之外)即將到來,因此CTE從不以特定順序返回比所需更多的行?

基本上只是談論ROW_NUMBER函數,它不會計算沒有返回的行的行號(如果我有數百萬行......),並且如果我要選擇CTE中的前100個,row_number仍然會被計算爲所選表格中的所有百萬行?

我已經嘗試過並且沒有在CTE語句中的「SELECT TOP(@top)」,在10.000次運行的循環中沒有看到時間使用的任何區別。儘管如此,我目前在桌上只有38000排。

編輯: 所以結果:

WITH t AS 
(
    **DO A TOP() WITH AN ORDER BY IN THE CTE** 
    SELECT TOP(@top) ID, name 
    ROW_NUMBER() OVER (ORDER BY ID) AS _row, 
    FROM dbo.User 
    ORDER BY ID 
) 
SELECT TOP(@pageSize) * 
**SELECTING TOP N FROM THE CTE, WHERE ROW-NUMBER IS ... DUE TO THE CTE IS IN ORDER ALREADY** 
FROM t 
WHERE t._row > (@[email protected]) 

這很可能是更有效的,如果我命令他們「倒退」,選擇CTE的「底部@pageSize」,這將離開了,而─條款......但如果實際速度更快,則需要進行一些測試......

回答

2

不鼓勵使用top而不使用order by。不能保證你會得到你想要的行,所以你不應該包含top。或者,您應該包含一個order by id,如果這是您想要的排序。

top的用戶不影響row_number()的計算,因爲該計算將在應用top之前完成。您可以想象在那裏有另一個窗口功能,例如sum() over()以瞭解top通常不能在row_number()之前應用,並且找到安全的情況是很困難的。

+0

當然,它需要在CTE內部使用頂層命令,否則您將獲得行的存儲順序,並基於您根據順序返回10的順序...所以,謝謝指出這一點。但是,如果該表有1億行,如何優化CTE?對我而言,當我讀取CTE時,它會選擇ID,名稱並計算表中每行1億行的行號,即使查詢的「最後部分」只選擇了行,這也會慢一點從15.000到15.010 ...? – 2014-12-06 16:30:51

+0

@ØyvindBerg。 。 。您*不會獲得排除'order by'命令時存儲行的順序。你會得到一個任意的順序,這通常可能與訪問方法有關,但是沒有,沒有,也沒有保證。 – 2014-12-06 17:24:32

+0

我並不是指第一個INSERTED行始終是第一個SELECTED,當省略「order by」時...但是按照順序,它們被「存儲」,其中存儲順序(「任意」順序)由諸如索引,等等......但我想我已經得到了這個,編輯了我的問題並且標記了你的回答。 「由於row_number()在頂部之前完成」... – 2014-12-06 19:14:15

1

如果您在ID上有支持索引,則不必閱讀並列舉整個表格。 SQL Server將必須讀取表格,幷包含所需的頁面。因此,如果您想要第1頁(第11到20行),則查詢將僅獲取20行。即使你不使用CTE的頂部,情況也是如此。

甲表的一些數據來測試:

create table dbo.[User] 
(
    ID int identity primary key, 
    Name nvarchar(128) 
) 

go 

insert into dbo.[User](Name) 
select top(1000) Name 
from sys.all_objects 

甲查詢,而無需多餘的頂部表達式。

DECLARE @pageSize INT, @currentPage INT, @top INT; 
SET @pageSize = 10; 
SET @currentPage = 1; 
SET @top = @pageSize * @currentPage + @pageSize; 
with C as 
(
    select U.ID, 
     U.Name, 
     row_number() over(order by U.ID) as rn 
    from dbo.[User] as U 
) 
select C.ID, 
     C.Name 
from C 
where C.rn > @pageSize * @currentPage and 
     C.rn <= @pageSize * (@currentPage + 1); 

這會給你這樣的查詢計劃:

enter image description here

各運營商的數量是行實際提取的數目。聚集索引掃描讀取由ID排序的20行。細分和順序項目枚舉行。 Top是確保提取不超過20行的操作符。過濾器刪除行1到10,並讓行11到20通過。

如果我們不是試圖獲得第5頁(@currentPage = 5讓行51〜60),該計劃將是這樣的:

enter image description here

Top運算符可以確保只有60行從聚集索引讀取並且過濾器過濾出前50行以返回最後10行。

對額外的頂級表達式使用您的最後一個查詢將不會添加任何有價值的內容。只有一個額外的冗餘頂級操作員

enter image description here

關鍵要了解什麼是在查詢計劃事情是要知道從左至右在時間要求苛刻的一行是執行完成。頂部操作員可以在返回足夠的行時停止聚簇索引掃描。

+0

數字等於取出的行數,很好(從未注意過那些)。所以;在這種情況下,CTE中的頂部並不是必需的,它實際上會產生開銷,因爲SQL已經基於「主要查詢」中的where-clause「預先考慮」來優化查詢。這一直是我的假設,所以這很好。我會自己嘗試一下,看看我需要CTE的各個列會發生什麼。另外一件事,當你添加「冗餘頂端」時,Clustered index使用98.2%的時間,對於「+0.1 top + 0.1%top」成本和0.1%segment的過濾只有1.2 ...或者SQL是「暖」? :P – 2014-12-08 22:12:09

+0

成本是在使用統計和啓發式執行查詢之前完成的**估計**。即使在實際執行計劃中,成本也是估算值。查詢優化器是基於成本的,這些數字是用來比較不同執行計劃的。它與查詢實際執行的方式無關。 – 2014-12-09 06:27:03

+0

「與它的實際執行方式無關」......但是對TOP的評估「更好」...所以我不是100%相信......但我相信你。 :) – 2014-12-09 08:13:15