13

ページングとUNION ALL複数のテーブルの使用についても助けが必要です。

UNION ALL特定の数の行のみを使用して返す複数のテーブルを結合するときに、最適化されたページングを実装するにはどうすればよいですか...


declare @startRow int
declare @PageCount int

set @startRow = 0
set @PageCount = 20

set rowcount @PageCount

select Row_Number() OVER(Order by col1) as RowNumber, col1, col2
from
(
    select col1, col2 from table1 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table2 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table3 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table4 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table5 where datetimeCol between (@dateFrom and @dateTo)
) as tmpTable
where RowNumber > @startRow

テーブル 3、4、および 5 には膨大な数の行 (数百万行) があり、テーブル 1 および 2 には数千行しかない場合があります。

startRow が「0」の場合、(Table1 から) 行 1 から 20 までのデータのみを期待します。正しい結果が得られますが、SQL Server がすべてのデータをフィルタリングしようとしている間、残りのテーブルのオーバーヘッドが高くなります....

@dateFrom と @dateTo の間隔が長いほど、結果セット全体から少数の行のみを取得しようとしているときに、クエリが大幅に遅くなります

同様のロジックを使用して、シンプルだがより良いアプローチを実装する方法を教えてください。:(

4

5 に答える 5

3

OFFSET FETCH 句の使用を検討してください (MSSQL 2012 以降で機能します)。

declare @startRow int
declare @PageCount int

set @startRow = 0
set @PageCount = 20


select col1, col2
from
(
    select col1, col2 from table1 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table2 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table3 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table4 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from table5 where datetimeCol between (@dateFrom and @dateTo)
) as tmpTable
order by col1
offset @startRow rows
fetch next @PageCount rows only

ここで言及したいのは、このクエリが常に O(n*log(n)) 時間かかる理由です。
このクエリを実行するには、データベースは次のことを行う必要があります。

  1. 複数のリストを 1 つのリストに統合するには、テーブルごとに O(n) 時間かかります。ここで、n - テーブル内の行の総数。
  2. リストを col1 で並べ替える - O(n*log(n)) を使用します。ここで、n - 行の総数です。
  3. リストをソート順にトラバースし、@startRow 行をスキップして、次の @PageCount 行を取得します。

ご覧のとおり、期待どおりの結果を得るには、すべてのデータを結合して並べ替える必要があります (番号 3)。

このクエリのパフォーマンスが依然として低く、パフォーマンスを向上させたい場合は、次のことを試してください。

  • すべてのテーブルで col1 に基づいてクラスター化されたインデックスを作成する
  • すべてのテーブルで col1 に基づいて非クラスター インデックスを作成し、**出力する他のすべての列を選択リストに含めます**。
于 2016-10-01T21:29:50.023 に答える
0
select col1, col2 from table1 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table2 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table3 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table4 where datetimeCol between (@dateFrom and @dateTo)
union all
select col1, col2 from table5 where datetimeCol between (@dateFrom and @dateTo)

ページングに使用するソートキーにインデックスがある場合、基本的には通常のテーブルと同じくらい効率的です。これは通常、すべてのテーブルがマージ連結されるクエリ プランになります。マージ連結はストリーミング操作です。コストは、テーブル内の行数ではなく、描画される行数に比例します。

SQL Server の行番号によるページングは​​、目的のウィンドウに到達するまで行を最初から最後まで列挙することによって常に機能します。行がテーブルから抽出されるか、複数のマージされたテーブルから抽出されるかは、根本的な違いにはなりません。

したがって、これを高速化する良い機会は、 をキーとするカバリング インデックスを作成することcol1です。残念ながら、between (@dateFrom and @dateTo)同時にインデックスを作成することはできません。したがって、両方のインデックス作成戦略を試して、最適なものを選択する必要があります。

于 2016-09-27T12:56:53.750 に答える
0

クラシックOFFSETベースのページングを適用する代わりに ( SQL Server 2012 でネイティブにサポートされるようになったことに注意してください)、特定のユース ケースでは、このブログ投稿で説明されているように、「シーク メソッド」と呼ばれることが多いメソッドから大きな利益が得られると思います。クエリは次のようになります。

select top 20 col1, col2
from
(
    select col1, col2 from t1 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t2 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t3 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t4 where datetimeCol between (@dateFrom and @dateTo)
    union all
    select col1, col2 from t5 where datetimeCol between (@dateFrom and @dateTo)
) as tmpTable
where (col1 > @lastValueForCol1)
   or (col1 = @lastValueForCol1 and col2 > @lastValueForCol2)
order by col1, col2

@lastValueForCol1との@lastValueForCol2値は、前のページの最後のレコードのそれぞれの値です。これにより、「次の」ページを取得できます。ORDER BY方向がの場合はDESC、単に<代わりに使用します。(col1, col2)が全体でグローバルに一意でない場合はtmpTable、クエリとWHEREandORDER BY句に別の列を追加して、ページ間でレコードが失われないようにする必要がある場合があります。

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

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

索引付け

「seek メソッド」を使用したページングは​​常に を使用する場合よりも高速ですが、各テーブルで がインデックス化されてOFFSETいることを確認する必要があります。(col1, col2)

于 2013-10-27T11:39:17.750 に答える
-1

テーブルはページングの結果セットで順序付けられるため (Union ALL はソートされません)、5 つのテーブルすべてから選択する理由はありません。コードを次のように変更する必要があります。

  • 表 1 からクエリを実行します。十分なレコードがあるかどうかを確認します。
  • そうでない場合は、表 2 から照会します。

毎回クエリされるレコードの数に応じて、オフセット数を管理します。このようにして、必要なテーブルのみをクエリします。

フィルターに従ってテーブル内のレコード数を選択して最適化し、そこからデータをクエリする必要があるかどうかを知ることもできます。したがって、レコード 30 ~ 50 が必要で、table1 に一致するレコードが 20 個しかない場合は、完全にスキップできます。

于 2015-08-20T07:44:29.733 に答える