web-dev-qa-db-ja.com

SQLサーバーにマージ結合でインデックスシークを使用させる

SQLサーバーでMerge結合をテストしています。私にはINNER JOINがあり、オプティマイザにMERGE JOINを実行させる:

  • Personal TableのIDは主キーです
  • Abteilung TableのIDが主キーです
  • これらのテーブルには他のインデックスはありません

     select * from  [dbo].[Personal] as P inner join [dbo].[Abteilung] as A  
     on  P.[ID]= A.[Personal_ID]   OPTION (MERGE JOIN) 
    

次に、オプティマイザーはこのプランを使用してクエリを実行します。

enter image description here

オプティマイザがindex seekではなくTable Scanを使用するように強制したい

この目的を達成するために私がしたこと:

  • IDPersonal_IDのTable Abteilungでグループ化インデックスを定義します。クエリを実行すると、実行プランは次のように変更されます。

enter image description here

ここでは、クラスター化インデックスのスキャンを行っていますが、クラスター化インデックスのシークは行っていません

  • FORCESEEKヒントの使用。このヒントを使用している場合、クエリを実行するとエラーが発生します。

このクエリでヒントが定義されているため、クエリプロセッサはクエリプランを作成できませんでした。ヒントを指定せず、SET FORCEPLANを使用せずにクエリを再送信します。

  • クエリにwhere句を追加すると、クエリは次のようになります。

      select * from  [dbo].[Personal] as P inner join [dbo].[Abteilung] as A  
      on P.[ID]= A.[Personal_ID] where A.[Personal_ID]=2 
      OPTION (MERGE    JOIN)
    

    enter image description here この状況では、クラスター化インデックススキャンしかありません。

私の質問:

クエリを変更して、クラスター化インデックスシークを最終行に表示するにはどうすればよいですか?

2
Kaja

クラスタ化インデックスシークを取得するには、フィルタをサポートするクラスタ化インデックスが必要です(たとえば、先頭のキーはIDではなくPersonal_IDである必要があります)。

フィルターをサポートするPersonal_IDの先行列を持つインデックスがない場合、シークを強制することはできません。

これは、これがテーブルに対して実行した唯一のクエリでない限り、既存のクラスター化インデックスを変更する必要があることを意味するものではありません。

また、Personal_IDをキー列として非クラスター化インデックスを作成することもできますが、SELECT *(本当に両方のテーブルのすべての列が必要ですか?)、とにかくクラスタ化インデックスから残りの列をフェッチする必要があります。 いくつかの行 返された、ある時点でシーク(まあ、シークに偽装した範囲スキャンに相当するもの)+ルックアップは通常のスキャンよりもコストがかかります。

なぜここでシークが必要だと思いますか?クエリが返す行の数、行の幅、およびかかる時間はどれくらいですか?これは単なる教育的なものですか、それともシークは常にスキャンよりもパフォーマンスが良いという前提の下にありますか? (そうではありません。)

いくつかの有用な読み物:

6
Aaron Bertrand

テストを作成しないと、特にSELECT *を使用してすべてのテーブル列を取得する場合を除いて、シーク述語がないとインデックスをシークすることは不可能だと思います。

シークしたいインデックスの一部であるインデックス列でフィルターをかけ、さらにこのインデックスでカバーされている列のみを取得しようとします。

ドキュメントでは、Merge joinは通常、スキャンするか、後でソートすることを説明しています。読む: https://technet.Microsoft.com/en-us/library/ms190967(v = sql.105).aspx

1
Magier