web-dev-qa-db-ja.com

UNIONALLを使用したサブクエリでのキールックアップの最適化

私は次のクエリを最適化しようとしています(実際にはもう少し複雑ですが、これは重要な部分です):

SELECT Id, StatusDate, [...Lot Of Columns...]
FROM ( (
        SELECT Id, StatusDate, [...Lot Of Columns...]
        FROM Results_201505
        WHERE A = 0, B = 1, C = 3
    ) UNION ALL (
        SELECT Id, StatusDate, [...Lot Of Columns...]
        FROM Results_201504
        WHERE A = 0, B = 1, C = 3
    ) UNION ALL (
        SELECT Id, StatusDate, [...Lot Of Columns...]
        FROM Results_201503
        WHERE A = 0, B = 1, C = 3
    )
) as result
ORDER BY StatusDate DESC
OFFSET xxx ROWS
FETCH NEXT 25 ROWS ONLY

すべてのテーブルで、「where」の部分をカバーするインデックスを作成しました。

CREATE NONCLUSTERED INDEX ix_asdf ON Results_asdf (StatusDate DESC)
INCLUDE (A, B, C)

WHEREの条件は、ユーザーが選択したフィルターによって変化するため、インデックスA, B, C, StatusDateを使用できません。 WHERE A=, B=, C=にすることもできますが、WHERE B=, D=またはWHERE C=にすることも、WHEREなしにすることもできます)。

私の考えでは、SQL Serverは私のインデックスを使用して高速インデックススキャンを実行し、キールックアップを可能な限り最後の瞬間(上位25行をフェッチする)まで遅らせます。これは、最も合理的な方法のように思えます。

実際、[... Lot of Columns...]をコメントアウトすると、すべてが計画どおりに進みます。インデックスが使用され、キーの検索は不要で、すべてが非常に高速です。

ただし、完全な形式では、SQL Server(SSMSで示されている実行プランによる)は、OFFSET ... FETCH 25の前にキールックアップを実行しようとします各行に対して

理由は明らかです(私は信じています)-Sql Serverは、UNION ALLが複数のテーブルを連結していることを認識しているため、UNIONの後、キールックアップを実行するには遅すぎます(UNION SQL Serverが認識しているため) IDのみですが、どの結果テーブルからのものかはわかりません)。 IDはグローバルに一意ですが、クエリオプティマイザはおそらくそれを認識していません。

もちろん、次のような方法でこれを回避することができます。

SELECT Id, StatusDate, [ some way to select columns from appropiate table depending on result.tbl ]
FROM (
    (
        SELECT Id, StatusDate, 'Results_201505' as tbl
        FROM Results_201505
        WHERE A = 0, B = 1, C = 3
    ) UNION ALL (
        SELECT Id, StatusDate, 'Results_201504' as tbl
        FROM Results_201504
        WHERE A = 0, B = 1, C = 3
    ) UNION ALL (
        SELECT Id, StatusDate, 'Results_201503' as tbl
        FROM Results_201503
        WHERE A = 0, B = 1, C = 3
    )
) as result
ORDER BY StatusDate DESC
OFFSET xxx ROWS
FETCH NEXT 25 ROWS ONLY

しかし、私の質問は-より良い方法はありますか?オプティマイザーへのある種のヒントは完璧ですが、それが不可能な場合、このクエリを処理するための最もエレガント/最速/最良の方法は何ですか?

1
MSM

これを試しましたか?

SELECT Id, StatusDate, [ some way to select columns from appropiate table depending on result.tbl ]
FROM (
        SELECT Id, StatusDate, 'Results_201505' as tbl
        FROM Results_201505
        WHERE A = 0, B = 1, C = 3
    UNION ALL 
        SELECT Id, StatusDate, 'Results_201504' as tbl
        FROM Results_201504
        WHERE A = 0, B = 1, C = 3
    UNION ALL 
        SELECT Id, StatusDate, 'Results_201503' as tbl
        FROM Results_201503
        WHERE A = 0, B = 1, C = 3
    ORDER BY StatusDate DESC
    OFFSET xxx ROWS
    FETCH NEXT 25 ROWS ONLY
) AS result ;
3
ypercubeᵀᴹ

ここで最も重要な最適化は、実際のクエリプランにCONVERT_IMPLICIT演算子が含まれていないことを確認することです。これは、UNIONALLされているテーブル間でわずかに異なるデータ型がある場合に発生する可能性があります。これにより、パフォーマンスが10倍から100倍低下する可能性があります。

最後の25行のみを取得する場合は、それぞれに25行のみを挿入する一時テーブルを作成できます。次に、FETCHは合計75の「ベスト25」を取得します(3つのテーブルがそれぞれ25行で結合されます)。

または、Enterprise Editionを使用している場合は、インデックス付きのパーティションビューを作成できます。

0
John Zabroski