2013-10-01 25 views
4

我在閱讀關於如何高效地瀏覽大型數據集,因爲我不滿意Row_NumberFetch是最差的。從這個sql server文章頁面的高效方式

這是文章: http://www.4guysfromrolla.com/webtech/042606-1.shtml

現在這篇文章有這樣一段代碼:

CREATE PROCEDURE [dbo].[usp_PageResults_NAI] 
(
    @startRowIndex int, 
    @maximumRows int 
) 
AS 

DECLARE @first_id int, @startRow int 

-- A check can be added to make sure @startRowIndex isn't > count(1) 
-- from employees before doing any actual work unless it is guaranteed 
-- the caller won't do that 

-- Get the first employeeID for our page of records 
SET ROWCOUNT @startRowIndex 
SELECT @first_id = employeeID FROM employees ORDER BY employeeid 

-- Now, set the row count to MaximumRows and get 
-- all records >= @first_id 
SET ROWCOUNT @maximumRows 

SELECT e.*, d.name as DepartmentName 
FROM employees e 
    INNER JOIN Departments D ON 
     e.DepartmentID = d.DepartmentID 
WHERE employeeid >= @first_id 
ORDER BY e.EmployeeID 

SET ROWCOUNT 0 

GO 

這個演示代碼看起來OK(如你看到:)其他演示)。上面的代碼只是因爲他在SELECT @first_id = employeeID FROM employees ORDER BY employeeid中使用Order By employeeid

比方說,我有一個名爲FirstName的字段,並希望以此來排序。那我如何寫上述程序?上述程序顯然不起作用,因爲那樣我們不能寫WHERE employeeid >= @first_id,因爲如果我們按名稱排序,我們不能得到first_id。這是因爲whereorder by之前執行。

如果我們改變上面的查詢:

Select * From (SELECT e.*, d.name as DepartmentName 
FROM employees e 
    INNER JOIN Departments D ON 
     e.DepartmentID = d.DepartmentID 
ORDER BY e.EmployeeID) v WHERE employeeid >= @first_id 

那麼它會工作,但這就意味着,上述查詢將給予更大的數據集表現極差。

那麼,我們如何將上述演示代碼用於生產使用?任何幫助表示讚賞。

+0

您對Departments.DepartmentID有PK(或唯一)嗎? –

+0

@Roman:這些不是我的表格。這是來自http://www.4guysfromrolla.com/webtech/042606-1.shtml的演示代碼,如問題所述。 –

+0

如果他們不是你的表,那麼你爲什麼問「那麼,我們如何使用上面的演示代碼進入生產使用?」擬議變更的目的是什麼?它不會在FirstName上排序。 – Paparazzi

回答

1

你的問題似乎是,你想使用另一種方法,因爲在大型數據集的非唯一字段排序時ROW_NUMBER不能很好地工作。然而,問題在於,在大數據集中對非唯一字段進行排序時,您想要使用的方法效果不佳,那麼您可以採取什麼措施來糾正?

答案是,但是你這樣做,除非你使用索引以及在非唯一的字段(S)分揀大型數據集不會表現良好。

只是爲了證明ROW_NUMBER比你甚至給上主鍵排序的簡單情況的方法好,我創建從你的鏈接模式,並添加下列步驟:

CREATE PROCEDURE dbo.usp_PagedResults_RowNumber 
(
    @startRowIndex int, 
    @maximumRows int 
) 
AS 
WITH Emp AS 
( SELECT e.*, rn = ROW_NUMBER() OVER(ORDER BY e.EmployeeID) 
    FROM employees e 
) 
SELECT TOP (@MaximumRows) 
     EmployeeID, 
     LastName, 
     FirstName, 
     e.DepartmentID, 
     Salary, 
     HireDate, 
     d.Name AS DepartmentName 
FROM Emp e 
     INNER JOIN Departments D ON 
      e.DepartmentID = d.DepartmentID 
WHERE rn >= @startRowIndex 
ORDER BY EmployeeID; 

然後我比較兩個查詢:

EXECUTE usp_PageResults_NAI 4500, 20; 
EXECUTE usp_PagedResults_RowNumber 4500, 20; 

首先看IO統計數據,您已發佈使用步驟如下:

Table 'Employees'. Scan count 1, logical reads 48 
(1 row(s) affected) 

(20 row(s) affected) 
Table 'Departments'. Scan count 1, logical reads 41 
Table 'Employees'. Scan count 1, logical reads 2 

(1 row(s) affected) 

所有統計數據與physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.結束所以我刪除這從所有這些以提高可讀性。使用ROW_NUMBER()時

統計:

(20 row(s) affected) 
Table 'Departments'. Scan count 1, logical reads 41 
Table 'Employees'. Scan count 1, logical reads 48 

(1 row(s) affected) 

沒有太多分開的兩個,但ROW_NUMBER進來少了一個索引掃描和2少邏輯讀取略勝一籌。

下一頁看執行計劃。使用行數在查詢的成本估計84%來自於:

enter image description here

而且ROW_NUMBER只有區區16%。

enter image description here

在實際執行時間,我不能畫中給出的數據的大小,合理的結論,因爲這兩個執行這麼快。我只能假設更簡單的計劃,而更低的IO也會使ROW_NUMBER更快。

因此,而不是試圖強制一個正方形插入一個圓孔,並使用此方法的ROWCOUNT來尋找一個字段,如FirstName,而是在FirstName上創建索引以幫助ROW_NUMBER進行排序。例如

CREATE NONCLUSTERED INDEX IX_Employees_FirstName ON dbo.Employees (FirstName ASC) INCLUDE (DepartmentID); 

爲了證明我跑這個查詢的區別:

SELECT EmployeeID, 
     DepartmentID, 
     RowNumber = ROW_NUMBER() OVER(ORDER BY FirstName, EmployeeID) 
FROM Employees; 

兩個添加索引之前:

以前

Table 'Employees'. Scan count 1, logical reads 501 

enter image description here

Table 'Employees'. Scan count 1, logical reads 249 

enter image description here

後這說明那種能夠使用索引減半的邏輯讀取數。

+0

我接受了你的回答,因爲我原來的問題本身沒有答案。雖然這不是我的問題的答案,因爲我已經只使用這種技術,並且我有200萬條記錄(這隻會隨着時間的推移而增加),並且大約需要3秒鐘從一個頁面遍歷到另一個頁面。不管怎麼說,多謝拉! –

0

我已經嘗試了多種不同的SP分頁方式,並且沒有效率地爲我工作。部分原因是在我的情況下,SP非常複雜,如果我使用所有條件,條件,排序,過濾等,速度就會變慢 - 所以我無法承受每個新的頁面請求都重複所有條件。

我實現它的方式 - 我運行它的所有條件,條件,排序,過濾等,但我像這樣只運行一次 - 並檢索所有的行,而不是所有的列,我檢索只是主鍵(這btw加快運行,比較所有列再生)。

在我的.NET代碼中,我將該列表存儲在Generic List (of integer)中。每當用戶請求一個新頁面時 - 我會向SP傳遞一個ID列表(一次說50個)。 SP然後,而不是執行完全搜索的條件,排序,過濾等簡單做一個INNER JOIN到列表。

這種方法結果非常有效,因爲所有條件 - 包括記錄的順序已經保存在ID列表中。它也很靈活,我可以隨時輕鬆地通過簡單地提供不同數量的ID來更改「每頁行數」。

+0

如果搜索返回100萬行,你下載它們全部?在6400萬,你會達到.NET 1 GB的限制。 – Paparazzi

+0

@布拉姆可以請你詳細說明數學嗎? –

+0

然後你告訴你有多少你可以緩存在.NET中,然後達到對象大小限制。告訴我需要多長時間將一百萬下載到緩存.NET列表中。而且你甚至不回答如何排序的問題。 – Paparazzi