2010-02-06 57 views
8

我寫了下面的代碼,它工作正常,但如果表中包含一百萬條記錄,則需要3秒才能完成。有沒有辦法來優化下面的代碼。T-sql:如何執行優化的尋呼?

DBCC DROPCLEANBUFFERS; 
DBCC FREEPROCCACHE; 

DECLARE @Page_Size int; 
DECLARE @Page_Number int; 
DECLARE @Lower_Bound int; 
DECLARE @Upper_Bound int; 

SET @Page_Size = 30; 
SET @Page_Number = 30000; 
SET @Lower_Bound = (@Page_Number - 1) * @Page_Size; 
--SET @Upper_Bound = @Page_Number * @Page_Size; 


WITH Customers AS--(Row_Numbr, Record_Id, First_Name, 
     Middle_Name, Last_Name, Email, Telephone) AS 
(

    SELECT ROW_NUMBER() 
     OVER 
     (ORDER BY Account.Customer.Record_Id) AS Row_Numbr, * 
    FROM Account.Customer 
) 

SELECT top(@Page_Size) * 
FROM Customers 
WHERE Row_Numbr > @Lower_Bound-- 
    AND Row_Numbr <= @Upper_Bound -- This is suppose to be faster 
--SELECT * FROM Customers 
--WHERE Row_Numbr > @Lower_Bound 
-- AND Row_Numbr <= @Upper_Bound 
+0

執行計劃說什麼? – super9 2010-02-06 18:56:33

+0

你有什麼指標?你能發佈創建表腳本嗎? – 2010-02-06 19:08:37

+0

相關:http://stackoverflow.com/questions/1897436/row-number-over-not-fast-enough-with-large-result-set-any-good-solution – 2010-02-06 20:28:03

回答

14

首先,爲什麼DBCC DROPCLEANBUFFERS;?這是緩衝池的冷重置。除非您想測量和調整硬盤驅動器的IO性能,否則沒有人關心冷藏緩存的性能。這是而不是您的系統將如何工作。在緩衝池中緩存頁面是數據庫中最關鍵的性能方面,然後您將其解決。它就像在沒有引擎的法拉利中出現並詢問爲何如此緩慢。對於性能測量,您應該完成與相反的:運行他查詢4-5次以預熱高速緩存,然後進行測量。

二,你的表結構是什麼?表Account.Customer表聚簇索引按Record_id排序?如果不是,那麼無論您如何表達T-SQL,都不會得到您想要的性能。

最後但並非最不重要,你有什麼系統?它是否有足夠的內存來緩存整個數據庫?如果不是,請購買更多內存。是否有其他進程競爭內存,如IIS/Asp?如果是,請將它們踢出自己的服務器,如果性能很重要,您應該n ever ever run the database on the same host as the web server

另一種可選擇的快速尋呼考慮鍵集驅動的解決方案:

/* moving up */ 
SELECT top(@Page_Size) * 
FROM Account.Customer 
WHERE Record_Id > @lastPageRecordId 
ORDER BY Record_Id; 

/* moving down */ 
SELECT top(@Page_Size) * 
FROM Account.Customer 
WHERE Record_Id < @firstPageRecordId 
ORDER BY Record_Id DESC; 

鍵集驅動的解決方案可以直接尋找到最後的位置,然後將掃描範圍內的下一個/前一頁,使用聚集索引鍵的位置。分頁邏輯(狀態)必須記住正在顯示的頁面上的最後一個和第一個鍵,以便從那裏繼續,而不是記住頁碼。

基於Rowcount的解決方案(以及MySQL中的LIMIT)效率低於基於鍵集的解決方案,因爲它們總是需要對記錄進行計數才能定位自己,而不是像鍵集那樣直接尋找位置。

+0

但是Record_Id可以是1,3,7,10,11,12,13,17。頁面將不相等,最後一頁也許不會覆蓋整個表格。 在這種情況下,如何獲得準確的@lastPageRecordId? 剛纔我知道你想用Record_Id索引增強性能,所以你想要我改變WITH子句並使用Record_Id而不是Row_Number,我不知道如何改變WITH子句呢!我跟着你嗎? – Costa 2010-02-07 05:38:19

+1

假設你想顯示3頁。第一次你說TOP(3)... ORDER BY RecordId,你得到1,3,7。下一頁你要求TOP(3)..WHERE RecordId> 7 ... ORDER BY ...,你得到10,11,12。要下去,你要求TOP(3)... WHERE RecordId <10 ... ORDER BY ... DESC,你又得到7,3,1。接下來你要求TOP(3)... WHERE RecordId> 12 ... ORDER BY ...,你得到13,17。您使用頁面上的第一個和最後一個RecordId作爲*鍵*,而不是等級。 – 2010-02-07 06:32:51

+0

@Remus嗨,鍵集解決方案比分頁更快嗎?我試圖創建一個基於上面的代碼存儲的存儲過程,但我沒有得到任何結果返回。有任何想法嗎? 創建PROC PagingSample \t((在)PAGE_SIZE INT, \t(AT)firstPageRecordId INT) AS 開始 \t選擇頂層((在)PAGE_SIZE)* \t FROM dbo.data \t WHERE(AT)( at)IDENTITY <(at)firstPageRecordId \t ORDER BY DateTime DESC; 結束 – 2012-06-19 16:47:34

2

我用這個存儲過程:

CREATE PROCEDURE sp_PagedItems 
    (
    @Page int, 
    @RecsPerPage int 
    ) 
AS 

-- We don't want to return the # of rows inserted 
-- into our temporary table, so turn NOCOUNT ON 
SET NOCOUNT ON 


--Create a temporary table 
CREATE TABLE #TempItems 
(
    ID int IDENTITY, 
    Name varchar(50), 
    Price currency 
) 


-- Insert the rows from tblItems into the temp. table 
INSERT INTO #TempItems (Name, Price) 
SELECT Name,Price FROM tblItem ORDER BY Price 

-- Find out the first and last record we want 
DECLARE @FirstRec int, @LastRec int 
SELECT @FirstRec = (@Page - 1) * @RecsPerPage 
SELECT @LastRec = (@Page * @RecsPerPage + 1) 

-- Now, return the set of paged records, plus, an indiciation of we 
-- have more records or not! 
SELECT *, 
     MoreRecords = 
    (
    SELECT COUNT(*) 
    FROM #TempItems TI 
    WHERE TI.ID >= @LastRec 
    ) 
FROM #TempItems 
WHERE ID > @FirstRec AND ID < @LastRec 


-- Turn NOCOUNT back OFF 
SET NOCOUNT OFF 
1

如果有人使用SQL Server 2012 - 訂的是增加了一個新功能BY子句,查詢一組數據的優化,使工作變得更加容易與數據分頁對於任何使用T-SQL編寫SQL Server中的整個執行計劃的人員。參考here