3

ページネーションを使用しようとしていますが、SOで完璧なリンクを取得しました

https://stackoverflow.com/a/109290/1481690

SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
          FROM      Orders
          WHERE     OrderDate >= '1980-01-01'
        ) AS RowConstrainedResult
WHERE   RowNum >= 1
    AND RowNum < 20
ORDER BY RowNum

まったく同じクエリを、内部クエリのいくつかのテーブルの追加結合で使用しようとしています。

次のシナリオでパフォーマンスの問題がほとんど発生していません

WHERE   RowNum >= 1
    AND RowNum < 20  ==>executes faster approx 2 sec


    WHERE   RowNum >= 1000
    AND RowNum < 1010      ==>  more time  approx 10 sec

    WHERE   RowNum >= 30000
    AND RowNum < 30010    ==> more time approx 17 sec

毎回10行を選択しますが、大きな時間差があります。アイデアや提案はありますか?

列を動的にバインドし、クエリを形成するため、このアプローチを選択しました。SQl Server 2008 でページネーション クエリを整理するための他の良い方法はありますか。

クエリのパフォーマンスを向上させる方法はありますか?

ありがとう

4

7 に答える 7

4

クエリでアクセスしているデータの量を常に確認し、不要な列と行を削除しようとします。これらは、すでにチェック済みかもしれないが、まだチェックしていない場合に備えて指摘したかった明らかなポイントです。あなたのクエリでは、「Select *」を実行しているため、パフォーマンスが低下している可能性があります。テーブルからすべての列を選択すると、適切な実行計画が得られません。選択した列のみが必要かどうかを確認し、テーブル Orders に正しいカバリング インデックスがあることを確認します。

明示的な SKIPP または OFFSET 関数は SQL 2008 バージョンでは使用できないため、作成する必要があり、INNER JOIN によって作成できます。1 つのクエリでは、最初に OrderDate で ID を生成し、そのクエリには他に何もありません。2 番目のクエリでも同じことを行いますが、ここではテーブル ORDER または ALL から他の関連する列を選択します (ALL 列が必要な場合)。次に、これを JOIN して ID と OrderDate で結果をクエリし、データ セットが必要な最小サイズである最初のクエリの ADD SKIPP 行フィルタを作成します。このコードを試してください。

    SELECT q2.*
    FROM
    (
        SELECT ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, OrderDate
        FROM      Orders
        WHERE     OrderDate >= '1980-01-01'
    )q1
    INNER JOIN 
    (
        SELECT ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
        FROM      Orders
        WHERE     OrderDate >= '1980-01-01'
    )q2
        ON q1.RowNum=q2.RowNum AND q1.OrderDate=q2.OrderDate AND q1.rownum BETWEEN 30000 AND 30020

見積もりを出すために、次のテスト データを使用してこれを試してみました。クエリを実行するウィンドウに関係なく、結果は 2 秒以内に返されます。テーブルはヒープ (インデックスなし) であることに注意してください。テーブルには合計 2M 行があります。test select は、50,000 から 50,010 までの 10 行を照会しています

以下の挿入には約 8 分かかりました。

    IF object_id('TestSelect','u') IS NOT NULL
        DROP TABLE TestSelect
    GO
    CREATE TABLE TestSelect
    (
        OrderDate   DATETIME2(2)
    )
    GO

    DECLARE @i bigint=1, @dt DATETIME2(2)='01/01/1700'
    WHILE @I<=2000000
    BEGIN

        IF @i%15 = 0
            SELECT @DT = DATEADD(DAY,1,@dt)

        INSERT INTO dbo.TestSelect( OrderDate )
        SELECT @dt

        SELECT @i=@i+1
    END

ウィンドウ 50,000 から 50,010 を選択するのに 3 秒もかかりませんでした。

最後の 1 行 2,000,000 から 2,000,000 を選択するのにも 3 秒かかりました。

    SELECT q2.*
    FROM
    (
        SELECT  ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum 
                ,OrderDate
        FROM TestSelect
        WHERE OrderDate >= '1700-01-01'
    )q1
    INNER JOIN
    (
        SELECT  ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum 
                ,*
        FROM TestSelect
        WHERE OrderDate >= '1700-01-01'
    )q2
        ON q1.RowNum=q2.RowNum 
        AND q1.OrderDate=q2.OrderDate 
        AND q1.RowNum BETWEEN 50000 AND 50010

ここに画像の説明を入力

于 2013-10-03T18:57:47.243 に答える
2

ROW_NUMBER操作のコストが大幅に増加するため、ページネーションを行うための安っぽい方法です。

代わりに、二重ORDER BY句を使用する必要があります。

でレコードを取得したいとしますROW_NUMBER between 1200 and 1210ROW_NUMBER() OVER (...)使用して後で結果をバインドする代わりに、次のようにWHEREする必要があります。

SELECT TOP(11) *
FROM (
    SELECT TOP(1210) *
    FROM [...]
    ORDER BY something ASC
) subQuery
ORDER BY something DESC.

このクエリは逆順で結果を返すことに注意してください。これは、一般的に言えば、UI でセットを簡単に逆にすることができるため、問題にはなりません。つまり、C# では、特に結果のセットが比較的小さいはずです。

後者は一般的にはるかに高速です。CREATE CLUSTERED INDEX ...後者のソリューションは、クエリの並べ替えに使用する列のCLUSTERING ( ) によって大幅に改善されることに注意してください。

それが役立つことを願っています。

于 2013-10-10T08:16:55.243 に答える
1

常に同じ数の行を選択しても、データ ウィンドウの最後で行を選択するとパフォーマンスが低下します。最初の 10 行を取得するために、エンジンは 10 行だけをフェッチします。次の 10 を取得するには、20 を取得し、最初の 10 を破棄して、10 を返す必要があります。

パフォーマンスを改善するためのいくつかのトリック (完全なリストではなく、OLAP の構築は完全に省略されています)。あなたは結合について言及しました。それが可能な場合は、内部クエリ内ではなく、その結果に参加します。また、取得するバケットに応じて、いくつかORDER BY OrderDateの ロジックを追加することもできます。「最後の」10を取得したい場合は、 はるかに高速に動作します。言うまでもなく、 index でなければなりません。ASCDESCORDER BY ... DESCorderDate

于 2013-10-01T14:32:46.367 に答える
1

信じられないことに、すべての SQL Server バージョンでページングを行うための最速の方法について言及した回答は他にありません。特に、ここでベンチマークされているように、ページ番号が大きいとオフセットが非常に遅くなる可能性があるという OP の質問に関してです。

SQL でページングを実行するには、まったく異なる、はるかに高速な方法があります。これは、こちらのブログ投稿で説明されているように、「シーク メソッド」と呼ばれることがよくあります。

SELECT TOP 10 *
FROM Orders
WHERE OrderDate >= '1980-01-01'
AND ((OrderDate > @previousOrderDate)
  OR (OrderDate = @previousOrderDate AND OrderId > @previousOrderId))
ORDER BY OrderDate ASC, OrderId ASC

@previousOrderDateとの@previousOrderId値は、前のページの最後のレコードのそれぞれの値です。これにより、「次の」ページを取得できます。ORDER BY方向がの場合はDESC、単に<代わりに使用します。

上記の方法では、最初に前の 40 レコードをフェッチしないと、すぐにページ 4 にジャンプすることはできません。しかし、多くの場合、とにかくそこまでジャンプしたくありません。代わりに、インデックス作成に応じて、データを一定時間で取得できる可能性があるはるかに高速なクエリを取得します。さらに、基になるデータが変更されても (たとえば、ページ 1 で、ページ 4 で)、ページは「安定」したままです。

これは、たとえば、Web アプリケーションでより多くのデータを遅延ロードする場合にページングを実装するための最良の方法です。

「seek メソッド」はkeyset pagingとも呼ばれます。

于 2013-10-26T18:07:07.257 に答える
0
declare @pageOffset int
declare @pageSize int
-- set variables at some point

declare @startRow int
set @startRow = @pageOffset * @pageSize

declare @endRow int
set @endRow + @pageSize - 1

SELECT 
    o.*
FROM
(
    SELECT 
        ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum
        , OrderId
    FROM      
        Orders
    WHERE     
        OrderDate >= '1980-01-01'
) q1
INNER JOIN Orders o
    on q1.OrderId = o.OrderId
where
    q1.RowNum between @startRow and @endRow
order by
    o.OrderDate
于 2013-10-05T04:17:40.373 に答える
0

@ peru、より良い方法があり、@ a1ex07によって提供された説明に基づいているかどうかに関して、次のことを試してください -

テーブルに数値 (order-id) や (order-date, order-index) などの一意の識別子があり、比較 (大なり、小なり) 操作を実行できる場合は、代わりにそれをオフセットとして使用します。行番号の。

たとえば、テーブル orders の主キーとして「order_id」がある場合 -
最初の 10 件の結果を取得するには -
1.

select RowNum, order_id from   
( select 
ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, 
o.order_id 
from orders o where  o.order_id > 0 ;  
) 
tmp_qry where RowNum between 1 and 10 order by RowNum; // first 10 

最後に返された order-id が 17 だったと仮定すると、

次の 10 を選択するには、
2.

select RowNum, order_id from   
( select 
ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, 
o.order_id 
from orders o where  o.order_id > 17 ;  
) 
tmp_qry where RowNum between 1 and 10 order by RowNum; // next 10 

行番号の値は変更されていないことに注意してください。比較されている order-id 値が変更されています。

そのようなキーが存在しない場合は、追加することを検討してください。

于 2013-10-09T18:48:04.377 に答える