SQL Serverのページネーション


17

約100 GBの非常に大きなデータベースがあります。私はクエリを実行しています:

select * from <table_name>;

100行目から200行目だけを表示したいです。

これが内部でどのように起こるかを理解したいです。データベースは、ディスクからすべてのレコードをメモリにフェッチし、100番目から400番目の行をクエリクライアントに送り返しますか?または、Bツリーなどのインデックス作成メカニズムを使用して、データベースからそれらのレコード(100番目から200番目)のみを取得するメカニズムが存在しますか?

これはページネーションの概念に関連していることがわかりましたが、データベースレベルで内部的にどのように発生するかを正確に見つけることができませんでした。

回答:


37

投稿したクエリで:

select * from <table_name>;

ORDER BYを指定しないため、100行目から200行目のようなものはありません。かなり多くの興味深い理由でORDER BYを含めない限り、順序は保証されませんが、それはここでのポイントではありません。

ポイントを説明するために、テーブルを使用しましょう。StackOverflowデータダンプの Usersテーブルを使用して、このクエリを実行します。

SELECT * FROM dbo.Users ORDER BY DisplayName;

既定では、DisplayNameフィールドにはインデックスがないため、SQL Serverはテーブル全体をスキャンし、DisplayNameで並べ替える必要があります。実行計画は次のとおりです。

ソート付きクラスター化インデックススキャン

それはきれいではありません-それは多くの作業で、推定サブツリーコストは約3万です。(PasteThePlanで選択演算子の上にマウスを置くと表示できます。)行100〜200のみが必要な場合はどうなりますか?SQL Server 2012+では次の構文を使用できます。

SELECT * FROM dbo.Users ORDER BY DisplayName OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY;

その実行計画もかなりです:

ソートとトップを使用したクラスター化インデックススキャン

SQL Serverは、テーブル全体をスキャンしてソートされたリストを作成し、行を100〜200行にするだけで、コストはまだ約3万です。さらに悪いことに、このリスト全体はクエリを実行するたびに再構築されます(結局、誰かがDisplayNameを変更した可能性があるためです)。

高速化するために、DisplayNameに非クラスター化インデックスを作成できます。これは、特定のフィールドでソートされたテーブルのコピーです。

CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName);

そのインデックスを使用して、クエリの実行プランはインデックスシークを実行します

インデックスシークとキールックアップ

クエリは即座に終了し、推定サブツリーコストはわずか0.66(30kではない)です。

要約すると、頻繁に実行するクエリをサポートする方法でデータを整理する場合、はい、SQL Serverはショートカットを使用してクエリを高速化できます。一方、ヒープまたはクラスター化インデックスしか持っていない場合は、手間がかかります。


「デフォルトでは、DisplayNameフィールドにはインデックスがないため、SQL Serverはテーブル全体をスキャンしてから、DisplayNameで並べ替える必要があります。」これが非常に基本的な質問である場合はご容赦ください。 「テーブル全体をスキャンする」と言いましたが、それはすべてのデータをメモリに入れてソートすることを意味しますか(これは正しい方法に見えません)?
AV94

あなたの答えから、フィールドがインデックス付けされている場合、次のようなクエリを作成することを理解しています-100行目から200行目を取得するのは非常に効率的です これが正しい理解かどうか教えてください。
AV94

最初の質問について@AnilVedala-はい、データをソートする必要があります。データベースは、ソートされていないリストで他にどのようにそれを達成できますか?
ブレントオザー

1
。あなたの2番目の質問について@AnilVedala - (。あなたは、実行計画を読んグラントFritcheyにより本実行計画をピックアップする方法について求めているならば)私はあなたを与えた最後の実行計画の出番だ
ブレントOzar

15

ソートを回避するために非カバーインデックスを使用するときのブレントの答えに加えて、以下を実行することで確認できる後のページ番号に潜在的な問題があります

SELECT * 
FROM dbo.Users 
ORDER BY DisplayName 
OFFSET 100000 ROWS 
FETCH NEXT 100 ROWS ONLY;

実行計画では、TOP演算子によって100行を除くすべての行が除外されても、ルックアップが100,100回実行されたことが示されています。

ここに画像の説明を入力してください

これは、以下のパターンを使用することにより、回避できます

WITH T
     AS (SELECT Id,
                DisplayName
         FROM   dbo.Users
         ORDER  BY DisplayName
        OFFSET 100000 ROWS 
        FETCH NEXT 100 ROWS ONLY
        )
SELECT U.*
FROM   dbo.Users U
       JOIN T
         ON U.Id = T.Id
ORDER  BY T.DisplayName 

これは、ルックアップを行う前に最後の100行を除くすべてを除外します。これは、大きなオフセット値の速度に大きな影響を与える可能性があります。

ここに画像の説明を入力してください


3

クエリ内でページネーションを実装する方法、データの性質、およびシステムの構成方法に大きく依存します。SQL Serverは、最小限の労力であると思われるものを使用してデータを返そうとすると言うのは非常に安全です。明示的な並べ替え順序、フィルタリング、グループ化、またはウィンドウイングがない場合、SQL Serverはクエリプランを最適化して、クエリに必要なデータを含むディスクからページだけを返すことができます。バッファプール。並べ替え、グループ化、ウィンドウ化、フィルタリングを含むようにクエリを変更し始めるとすぐに、複雑になり始めます。

SQLパフォーマンスに非常に良い記事があり、ここでいくつかのページ付けの様々な方法の詳細と、それらがどのようにクエリプランに影響を与えるに入ります。それを読んでから、それらが指摘するさまざまな方法のいくつかを試してみて、どのクエリプランが自分のシステムで選択されているかを確認することを強くお勧めします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.