2014-10-16 123 views
3

我正在用SQL Server進行分頁,我想通過將結果的總數作爲部分結果集的一部分來避免重複,而不是獲取該結果集,然後執行單獨的查詢以獲取計數。但是,麻煩在於,它似乎在增加執行時間。例如,如果我請與SET STATISTICS TIME ON,這樣的:在CTE內部使用COUNT()比在CTE之外更昂貴嗎?

WITH PagedResults AS (
    SELECT 
     ROW_NUMBER() OVER (ORDER BY AggregateId ASC) AS RowNumber, 
     COUNT(PK_MatrixItemId) OVER() AS TotalRowCount, 
     * 
    FROM [MyTable] myTbl WITH(NOLOCK) 
) 
SELECT * FROM PagedResults 
WHERE RowNumber BETWEEN 3 AND 4810 

...或者這個(其執行計劃是相同的):

SELECT * FROM (
    SELECT TOP (4813) 
     ROW_NUMBER() OVER (ORDER BY AggregateId ASC) AS RowNumber, 
     COUNT(PK_MatrixItemId) OVER() AS TotalRowCount, 
     * 
    FROM [MyTable] myTbl WITH(NOLOCK) 
) PagedResults 
WHERE PagedResults.RowNumber BETWEEN 3 AND 4810 

...似乎是一個平均的CPU時間(全查詢的1.5〜2倍加起來)不亞於此:

SELECT * FROM (
    SELECT TOP (4813) 
     ROW_NUMBER() OVER (ORDER BY AggregateId ASC) AS RowNumber, 
     * 
    FROM [MyTable] myTbl WITH(NOLOCK) 
) PagedResults 
WHERE PagedResults.RowNumber BETWEEN 3 AND 4810 

SELECT COUNT(*) FROM [MyTable] myTbl WITH(NOLOCK) 

很顯然,我寧願用前者比後者,因爲後者冗餘重複FROM條款(和W如果有的話,可以重複任何WHERE條款),但是它的執行時間要好得多,我真的必須使用它。有什麼辦法可以讓我減少前者的執行時間?

+0

你從執行計劃中得到了什麼?理解你的選擇並給出一些關於解釋的暗示應該是有用的。 – Paolo 2014-10-16 09:59:26

+0

這很難說,因爲執行計劃是非常不同的,但據我所見,前者的執行計劃包含一個「嵌套循環」階段,將count列連接到其餘的,這有一個很大的開銷。 – Jez 2014-10-16 10:08:12

回答

1

CTE被內聯到查詢計劃中。它們的執行方式與派生表完全相同。

派生表格不對應於物理操作。它們不會將結果集「實現」到臨時表中。 (我相信MySQL會這樣做,但MySQL是關於最原始的主流RDBMS的。)

使用OVER()在查詢計劃中確實表現爲緩衝到臨時表。目前還不清楚爲什麼這裏的速度會比重新讀取底層表的速度更快。緩衝比較慢,因爲寫入比在SQL Server中讀取更多的CPU資源。我們只能從原始表格中讀取兩次。這可能就是爲什麼後者更快。

如果要避免重複查詢的某些部分,請使用視圖或表值函數。當然,這些並不是一次性查詢的好選擇。您還可以在應用程序層中生成SQL並重用字符串。 ORM也使這更容易。

+0

它可能不會更快,但爲什麼它更慢? – Jez 2014-10-16 10:18:39

+0

由於緩衝。 – usr 2014-10-16 10:24:05