4

大規模なデータセットを効率的にページングする方法について読んでいます。満足できず、最悪だRow_Numberからです。Fetch

これは記事です: 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 

このデモ コードは問題ないように見えます (他のデモと同様に :))。上記のコードは、彼が in を使用Order By employeeidしているためにのみ機能しSELECT @first_id = employeeID FROM employees ORDER BY employeeidます。

FirstNameという名前のフィールドがあり、代わりにそれで注文したいとしましょう。では、上記の手順をどのように記述すればよいでしょうか。上記の手順は明らかに機能しません。なぜなら、名前で並べ替えるとWHERE employeeid >= @first_id取得できないため、書き込むことができないからです。first_idこれはwhereの前に が実行されるためですorder 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

それは機能しますが、これは、上記のクエリが大規模なデータ セットで非常にパフォーマンスが低下することを意味します。

では、上記のデモ コードを本番環境で使用するにはどうすればよいでしょうか。どんな助けでも大歓迎です。

4

2 に答える 2

1

ROW_NUMBERあなたの質問は、大規模なデータセットで一意でないフィールドを並べ替えるときにうまく機能しないため、別の方法を使用したいということです。ただし、問題は、大規模なデータ セット内の一意でないフィールドを並べ替えるときに、使用したい方法がうまく機能しないことです。それを修正するにはどうすればよいでしょうか?

答えは、インデックスを適切に使用しない限り、一意でないフィールドで大規模なデータセットを並べ替えると、うまく機能しないということです。

主キーでソートする単純なケースでも、指定した方法よりも優れていることを示すため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;

次に、2 つのクエリを比較しました。

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)

この 2 つを区別することはあまりありませんが、ROW_NUMBER は、インデックス スキャンが 1 回少なく、論理読み取りが 2 回少ないため、わずかに優れています。

次に実行計画を見てみましょう。Rowcount を使用すると、クエリ コストの推定 84% が発生します。

ここに画像の説明を入力

そして ROW_NUMBER はわずか 16% です。

ここに画像の説明を入力

実際の実行時間に関しては、どちらも非常に高速に実行されるため、データのサイズを考えると合理的な結論を導き出すことはできません。より単純な計画しか想定できず、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

ここに画像の説明を入力

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

ここに画像の説明を入力

これは、並べ替えが論理読み取りの数を半分にするインデックスを使用できることを示しています。

于 2013-10-01T13:51:34.143 に答える
0

SP ページングの複数の異なる方法を試しましたが、効率的に機能するものはありませんでした。理由の一部は、私の場合、SP が非常に複雑であり、すべての基準、条件、並べ替え、フィルタリングなどを使用すると実行速度が遅くなるため、新しいページ要求ごとにそれらすべてを繰り返す余裕がありませんでした。

私が実装した方法-すべての基準、条件、並べ替え、フィルタリングなどを使用して実行しますが、このように実行するのは1回だけです-すべての行を取得しますが、すべての列を取得する代わりに、プライマリのみを取得しますキー(すべての列の再取得と比較して、実行が高速化されます)。

私の .NET コードでは、そのリストをGeneric List (of integer). ユーザーが新しいページを要求するたびに、ID のリスト (一度に 50 など) を SP に渡します。次に、SP は、条件、並べ替え、フィルタリングなどで完全な検索を実行する代わりに、単純にそのリストに INNER JOIN を実行します。

レコードの順序を含むすべての条件が ID のリストに既に保存されているため、このアプローチは非常に効率的であることが判明しました。柔軟性も高く、一度に異なる数の ID を指定するだけで、いつでも「ページあたりの行数」を簡単に変更できます。

于 2013-10-01T13:56:15.210 に答える