宣言された結合列の順序を変更するとソートが導入されるのはなぜですか?


40

同じ名前、タイプ、およびインデックスキー列を持つ2つのテーブルがあります。それらの1つには一意のクラスター化インデックスがあり、もう1つには非一意があります。

テストのセットアップ

いくつかの現実的な統計を含むセットアップスクリプト:

DROP TABLE IF EXISTS #left;
DROP TABLE IF EXISTS #right;

CREATE TABLE #left (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE UNIQUE CLUSTERED INDEX IX ON #left (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #left WITH ROWCOUNT=63800000, PAGECOUNT=186000;

CREATE TABLE #right (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE CLUSTERED INDEX IX ON #right (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #right WITH ROWCOUNT=55700000, PAGECOUNT=128000;

再現

これら2つのテーブルをクラスタリングキーで結合するとき、次のように1対多のMERGE結合が期待されます。

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.a=r.a AND
    l.b=r.b AND
    l.c=r.c AND
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

これは私が欲しいクエリプランです:

これは私が欲しいものです。

(警告を気にせず、偽の統計に関係します。)

ただし、次のように、結合内で列の順序を変更すると、

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.c=r.c AND     -- used to be third
    l.a=r.a AND     -- used to be first
    l.b=r.b AND     -- used to be second
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

...これは起こります:

結合で宣言された列の順序を変更した後のクエリプラン。

ソート演算子は、結合の宣言された順序に従ってストリームを並べているようです。つまりc, a, b, d, e, f, g, h、クエリプランにブロッキング操作が追加されます。

私が見たもの

  • 列をNOT NULLに変更してみましたが、同じ結果になりました。
  • 元のテーブルはで作成されましたがANSI_PADDING OFF、で作成してANSI_PADDING ONもこの計画には影響しません。
  • INNER JOIN代わりにLEFT JOIN、変更を試みました。
  • 2014 SP2 Enterpriseでそれを発見し、2017 Developer(現在のCU)で再現を作成しました。
  • 主要なインデックス列のWHERE句を削除することで適切なプランが生成されますが、結果に多少影響します。

最後に、質問に行きます

  • これは意図的なものですか?
  • クエリを変更せずに並べ替えを削除できますか(これはベンダーコードなので、実際にはそうではありません...)。テーブルとインデックスを変更できます。

回答:


28

これは意図的なものですか?

そうです、そうです。残念ながら、MicrosoftがConnectフィードバックサイトを廃止し、SQL Serverチームの開発者からの多くの有用なコメントを削除したため、このアサーションの最良の公開ソースは失われました。

とにかく、現在のオプティマイザーの設計では、不必要な並べ替え自体積極的に回避しようとはしていません。これはウィンドウ関数などで最もよく発生しますが、順序、特に演算子間の保存順序に敏感な他の演算子でも見られます。

それにもかかわらず、オプティマイザは不必要なソートを回避するのに非常に優れています(多くの場合)が、この結果は通常、異なる順序の組み合わせを積極的に試みる以外の理由で発生します。その意味では、許容可能なコストで一般的なプランの品質を向上させることが示されている直交オプティマイザー機能間の複雑な相互作用であるため、「検索スペース」の問題ではありません。

たとえば、順序付け要件(たとえば、最上位ORDER BY)を既存のインデックスに単純に一致させることで、多くの場合、並べ替えを回避できます。あなたの場合、それは追加することを意味する可能性がありますORDER BY l.a, l.b, l.c, l.d, l.e, l.f, l.g, l.h;が、これは過度に単純化されています(そしてクエリを変更したくないので受け入れられません)。

より一般的には、各メモグループは、入力順序を含む必要なまたは望ましいプロパティに関連付けることができます。特定の順序を強制する明白な理由がない場合(例えば、を満たすためORDER BY、または順序に敏感な物理演算子から正しい結果を保証するため)、「運」の要素が含まれます。Merge Join ConcatenationでSortsを回避するで(結合モードまたは結合モードで)結合を結合することに関する詳細を記述しました。その多くは、製品のサポートされている表面積を超えているため、情報として扱い、変更される可能性があります。

特定のケースでは、はい、jadarnel27が示唆するようにソートを回避するためにインデックス調整できます。ここで実際にマージ結合を好む理由はほとんどありません。OPTION(HASH JOIN, LOOP JOIN)データの知識に応じて、クエリを変更せずにプランガイドを使用して、ハッシュまたはループの物理結合を選択することや、最高、最低、平均ケースのパフォーマンスのトレードオフを示唆することもできます。

最後に、好奇心として、単純なORDER BY l.bでソートを回避できる可能性があることに注意してください。ただしb、複雑な残差を伴う単独での潜在的に効率の低い多対多のマージ結合が犠牲になります。これは主に、前述のオプティマイザー機能間の相互作用の例として、またトップレベルの要件が伝播する方法として説明しています。


19

クエリを変更せずに並べ替えを削除できますか(これはベンダーコードなので、実際にはそうではありません...)。テーブルとインデックスを変更できます。

インデックスを変更できる場合#rightは、結合のフィルターの順序に一致するようにインデックスの順序を変更すると、並べ替えが削除されます(私にとって)。

CREATE CLUSTERED INDEX IX ON #right (c, a, b, d, e, f, g, h)

驚いたことに(少なくとも、私にとって)、これはどちらのクエリも並べ替えになりません。

これは意図的なものですか?

いくつかの奇妙なトレースフラグからの出力を見ると、最終的なメモ構造に興味深い違いがあります。

各クエリの最終的なメモ構造のスクリーンショット

上部の「ルートグループ」で確認できるように、両方のクエリには、このクエリを実行するための主要な物理操作としてマージ結合を使用するオプションがあります。

良いクエリ

ソートなしの結合は、グループ29のオプション1およびグループ31のオプション1によって駆動されます(それぞれが関連するインデックスの範囲スキャンです)。これは、グループ27(図示せず)でフィルター処理されます。これは、結合をフィルター処理する一連の論理比較操作です。

不正なクエリ

1 ソートは、これら二つのグループ(29,31)の各々が有する(新しい)オプション3によって駆動されます。オプション3は、前述の範囲スキャンの結果に対して物理的な並べ替えを実行します(これらの各グループのオプション1)。

どうして?

何らかの理由で、29.1および31.1をマージ結合のソースとして直接使用するオプションは、2番目のクエリのオプティマイザーでも使用できません。そうでなければ、他のオプションの中でルートグループの下にリストされると思います。もしそれが利用可能であれば、それは間違いなく非常に高価なソート操作よりもそれらを選ぶでしょう。

次のいずれかのみを結論付けることができます。

  • これは、オプティマイザーの検索アルゴリズムのバグ(または制限の可能性が高い)です。
    • 5つのキーのみを持つようにインデックスと結合を変更すると、2番目のクエリのソートが削除されます(6、7、および8キーはすべてソートされます)。
    • これは、8つのキーを持つ検索スペースが非常に大きいため、オプティマイザが「十分な計画が見つかりました」という理由で早期に終了する前に、非ソートソリューションを実行可能なオプションとして識別する時間がないことを意味します。
    • 結合条件の順序がオプティマイザーの検索プロセスにこれほど影響を与えるということは、私には少しバグがあるように見えますが、実際にはそれが少し頭にあります
  • 結果の正確さを保証するためにソートが必要です
    • キーが少ない場合、またはキーが別の順序で指定されている場合、クエリはソートなしで実行できるため、これは考えにくい

誰かがやって来て、なぜソートが必要なのを説明できることを願っていますが、メモの建物の違いは答えとして投稿するのに十分面白いと思いました。


1
サーチスペースに関するあなたのコメントは、実際ここにあると思います。インデックスのみを使用するには、オプティマイザーは条件に十分であることを確認する必要があります。過去5つのキーでは、フォールバックする前にチェックする可能性が多すぎます。クエリの順序のすべての組み合わせが列挙されている場合、オプティマイザーが成功するかフォールバックするかを知りたいのですが、
Mind.Mindor

そして、確かに矛盾は少しバグのように見えますが、おそらくインデックスが十分であることを検証するために使用されるアルゴリズムに完全に依存しています。すべての組み合わせをテストした場合、おそらく結果でパターンを確認し、使用されているアルゴリズムを判別できるでしょう。もっと典型的なユースケースに最適に動作するように書かれているに違いありません。制限時間内に8キーソリューションを確実に見つけることができる代替手段が存在する場合がありますが、3〜4個未満のキーがある場合、現在のソリューションよりも遅くなります。
ミンダー氏
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.