web-dev-qa-db-ja.com

SQL Serverでの内部結合と左側結合のパフォーマンス

9つのテーブルに対してINNER JOINを使用するSQLコマンドを作成しましたが、とにかくこのコマンドには非常に長い時間がかかります(5分以上)。したがって、私の知っていることにもかかわらず、初めてLEFT JOINのパフォーマンスが向上したため、私の担当者はINNER JOINをLEFT JOINに変更することを推奨します。変更した後、クエリの速度が大幅に向上しました。

LEFT JOINがINNER JOINより速いのはなぜですか?

私のSQLコマンドは次のようになります。SELECT * FROM A INNER JOIN B ON ... INNER JOIN C ON ... INNER JOIN Dなど

更新:これは私のスキーマの簡単な説明です。

FROM sidisaleshdrmly a -- NOT HAVE PK AND FK
    INNER JOIN sidisalesdetmly b -- THIS TABLE ALSO HAVE NO PK AND FK
        ON a.CompanyCd = b.CompanyCd 
           AND a.SPRNo = b.SPRNo 
           AND a.SuffixNo = b.SuffixNo 
           AND a.dnno = b.dnno
    INNER JOIN exFSlipDet h -- PK = CompanyCd, FSlipNo, FSlipSuffix, FSlipLine
        ON a.CompanyCd = h.CompanyCd
           AND a.sprno = h.AcctSPRNo
    INNER JOIN exFSlipHdr c -- PK = CompanyCd, FSlipNo, FSlipSuffix
        ON c.CompanyCd = h.CompanyCd
           AND c.FSlipNo = h.FSlipNo 
           AND c.FSlipSuffix = h.FSlipSuffix 
    INNER JOIN coMappingExpParty d -- NO PK AND FK
        ON c.CompanyCd = d.CompanyCd
           AND c.CountryCd = d.CountryCd 
    INNER JOIN coProduct e -- PK = CompanyCd, ProductSalesCd
        ON b.CompanyCd = e.CompanyCd
           AND b.ProductSalesCd = e.ProductSalesCd 
    LEFT JOIN coUOM i -- PK = UOMId
        ON h.UOMId = i.UOMId 
    INNER JOIN coProductOldInformation j -- PK = CompanyCd, BFStatus, SpecCd
        ON a.CompanyCd = j.CompanyCd
            AND b.BFStatus = j.BFStatus
            AND b.ProductSalesCd = j.ProductSalesCd
    INNER JOIN coProductGroup1 g1 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup1Cd
        ON e.ProductGroup1Cd  = g1.ProductGroup1Cd
    INNER JOIN coProductGroup2 g2 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup2Cd
        ON e.ProductGroup1Cd  = g2.ProductGroup1Cd
235
Anonymous

LEFT JOININNER JOINよりも絶対に高速ではありません。実際、遅いです。定義により、外部結合(LEFT JOINまたはRIGHT JOIN)は、INNER JOINのすべての作業に加えて、結果をnull拡張する余分な作業を行う必要があります。また、結果セットのサイズが大きいために、より多くの行が返され、合計実行時間がさらに増加することも予想されます。

(そして、LEFT JOINwereが、想像しにくい要因の合流のためにspecificの状況で速くなったとしても、機能的にはINNER JOINなので、あるインスタンスのすべてのインスタンスを他のインスタンスに単純に置き換えることはできません!)

ほとんどの場合、パフォーマンスの問題は、候補キーまたは外部キーが適切にインデックス付けされていないなど、他の場所にあります。 9つのテーブルを結合するのは非常に多いため、文字通りスローダウンはほぼどこでも発生する可能性があります。スキーマを投稿する場合、詳細を提供できる場合があります。


編集:

これをさらに振り返ると、LEFT JOININNER JOINより速いかもしれない状況を考えることができます。

  • 一部のテーブルはvery small(たとえば、10行未満)です。
  • テーブルには、クエリをカバーするのに十分なインデックスがありません。

この例を考えてみましょう:

CREATE TABLE #Test1
(
    ID int NOT NULL PRIMARY KEY,
    Name varchar(50) NOT NULL
)
INSERT #Test1 (ID, Name) VALUES (1, 'One')
INSERT #Test1 (ID, Name) VALUES (2, 'Two')
INSERT #Test1 (ID, Name) VALUES (3, 'Three')
INSERT #Test1 (ID, Name) VALUES (4, 'Four')
INSERT #Test1 (ID, Name) VALUES (5, 'Five')

CREATE TABLE #Test2
(
    ID int NOT NULL PRIMARY KEY,
    Name varchar(50) NOT NULL
)
INSERT #Test2 (ID, Name) VALUES (1, 'One')
INSERT #Test2 (ID, Name) VALUES (2, 'Two')
INSERT #Test2 (ID, Name) VALUES (3, 'Three')
INSERT #Test2 (ID, Name) VALUES (4, 'Four')
INSERT #Test2 (ID, Name) VALUES (5, 'Five')

SELECT *
FROM #Test1 t1
INNER JOIN #Test2 t2
ON t2.Name = t1.Name

SELECT *
FROM #Test1 t1
LEFT JOIN #Test2 t2
ON t2.Name = t1.Name

DROP TABLE #Test1
DROP TABLE #Test2

これを実行して実行計画を表示すると、INNER JOINクエリは実際にはLEFT JOINよりもコストが高いことがわかります。これは、上記の2つの条件を満たすためです。これは、SQL ServerがINNER JOINに対してハッシュ一致を行いたいが、LEFT JOINに対してネストされたループを実行するためです。前者は通常より高速ですが、行数が非常に少ないのでand使用するインデックスがないため、ハッシュ操作が最も高価な部分になりますクエリ。

お気に入りのプログラミング言語でプログラムを作成して、5つの要素を持つリストに対して5つの要素を持つハッシュテーブルに対して多数のルックアップを実行することで、同じ効果を確認できます。サイズが大きいため、ハッシュテーブルのバージョンは実際には低速です。しかし、ハッシュテーブルのO(N)対O(1)であるため、50要素または5000要素に増やすと、リストバージョンのクロールが遅くなります。

ただし、このクエリをIDではなくName列に変更すると、非常に異なるストーリーが表示されます。その場合、それは両方のクエリに対してネストされたループを実行しますが、INNER JOINバージョンはクラスター化インデックススキャンの1つをシークに置き換えることができます-これは文字通りaの一桁 =多数の行があると高速になります。

したがって、結論は多かれ少なかれ、上記のいくつかの段落で述べたものです。これはほぼ確実に、インデックス付けまたはインデックスカバレッジの問題であり、1つ以上の非常に小さなテーブルと組み合わされる可能性があります。これらは、SQL Server mightINNER JOINよりもLEFT JOINに対してより悪い実行計画を選択する唯一の状況です。

385
Aaronaught

まだ説明されていない内部結合よりも外部結合が高速になる可能性がある重要なシナリオが1つあります。

外部結合を使用する場合、結合列が外部表のPKであり、外部表から選択された列がない場合、オプティマイザーは常に実行計画から外部結合表を自由にドロップできます。例えばSELECT A.* FROM A LEFT OUTER JOIN B ON A.KEY=B.KEYとB.KEYはBのPKです。Oracle(私はリリース10を使用していたと思います)とSql Server(私は2008 R2を使用しました)の両方実行プランからテーブルBを整理します。

同じことが内部結合にも必ずしも当てはまりません。SELECT A.* FROM A INNER JOIN B ON A.KEY=B.KEYは、存在する制約に応じて実行計画でBを必要とする場合としない場合があります。

A.KEYがB.KEYを参照するNULL可能外部キーである場合、オプティマイザーはA行ごとにB行が存在することを確認する必要があるため、計画からBをドロップできません。

A.KEYがB.KEYを参照する必須の外部キーである場合、制約によって行の存在が保証されるため、オプティマイザはBを計画から自由に削除できます。しかし、オプティマイザが計画からテーブルを削除できるからといって、そうなるわけではありません。 SQL Server 2008 R2は、計画からBを削除しません。 Oracle 10 DOESは計画からBを落とします。この場合、外部結合がSQL Serverの内部結合をどのように上回るかを確認するのは簡単です。

これは些細な例ですが、スタンドアロンのクエリには実用的ではありません。あなたがする必要がないのにどうしてテーブルに参加するのですか?

しかし、これはビューを設計するときに非常に重要な設計上の考慮事項になる可能性があります。多くの場合、中央テーブルに関連してユーザーが必要とする可能性があるすべてのものを結合する、「何でもする」ビューが作成されます。 (特に、リレーショナルモデルを理解しないアドホッククエリを実行している素朴なユーザーがいる場合)ビューには、多数のテーブルから関連するすべての列を含めることができます。しかし、エンドユーザーはビュー内のテーブルのサブセットからの列にのみアクセスできます。表が外部結合で結合されている場合、オプティマイザは不要な表を計画から削除できます(そして削除します)。

外部結合を使用したビューで正しい結果が得られることを確認することが重要です。 Aaronaughtが言ったように - あなたは盲目的にOUTER JOINをINNER JOINに置き換えて同じ結果を期待することはできません。しかし、ビューを使用するときにパフォーマンス上の理由で役立つ場合があります。

最後の注意 - 私は上記のことを考慮してパフォーマンスへの影響をテストしていません、しかし理論的には条件<FOREIGN_KEY> IS where句のNOT NULLです。

110
dbenham

すべてが想定外の動作をする場合、特にクエリオプティマイザ、クエリプランのキャッシュ、および統計に関しては、すべてが想定どおりに動作しないことがわかっています。

まず、インデックスと統計を再構築し、次にクエリプランキャッシュをクリアして、問題が発生しないようにします。しかし、それが終わっても問題を経験しました。

私は左結合が内部結合より速いといういくつかのケースを経験しました。

根本的な理由はこれです:あなたが2つのテーブルを持っていて、(両方のテーブルで)インデックスを使ってカラムにジョインするなら。テーブル1のインデックス内のエントリをループしてテーブル2のインデックスと一致した場合でも、逆の場合と同じ結果になります。テーブル2のインデックス内のエントリをループしてインデックスと一致した場合表1に。問題は、誤解を招くような統計がある場合、クエリオプティマイザはインデックスの統計を使用して、(他の基準に基づいて)最も一致しないエントリを持つテーブルを見つけます。それぞれ100万個のテーブルが2つある場合、テーブル1では10行が一致し、テーブル2では100000行が一致します。最善の方法は、表1で索引スキャンを行い、表2で10回マッチングすることです。その逆は、100000行をループし、100000回のマッチングを試みても10回しか成功しないインデックススキャンです。そのため、統計が正しくない場合、オプティマイザは間違ったテーブルとインデックスを選択してループオーバーする可能性があります。

オプティマイザが左の結合を記述された順序で最適化することを選択した場合は、内部結合よりもパフォーマンスが向上します。

しかし、オプティマイザは左の結合を左の準結合として準最適に最適化することもできます。目的のものを選択するには、強制注文のヒントを使用できます。

21
Kvasi

最後にOPTION (FORCE ORDER)を使って両方のクエリ(内部結合と左結合を持つもの)を試して、結果を投稿してください。 OPTION (FORCE ORDER)は、クエリで指定した結合順序を使用してオプティマイザに実行計画を構築させるクエリヒントです。

INNER JOINLEFT JOINと同じくらい速く実行を開始するならば、それは理由です:

  • INNER JOINsだけで構成されたクエリでは、結合の順序は関係ありません。これにより、クエリオプティマイザが適切と判断したときに結合を順序付けることができるため、問題はオプティマイザに依存する可能性があります。
  • LEFT JOINでは、結合順序を変更するとクエリの結果が変わるため、そうではありません。つまり、エンジンはクエリで指定した結合順序に従う必要があります。これは最適化された結合順序よりも優れている可能性があります。

これがあなたの質問に答えているかどうかはわかりませんが、私はかつてオプティマイザをめちゃくちゃにした非常に複雑なクエリが計算を行うことを特徴とするプロジェクトにいました。 FORCE ORDERを使用すると、クエリの実行時間が5分から10秒に短縮されることがありました。

16
Francisco Pires

左外部結合と内部結合の間でいくつかの比較を行ってきましたが、一貫した違いを見つけることができませんでした。たくさんの変数があります。何千ものテーブルと、多数のフィールドを持ち、時間の経過とともに多くの変更があるレポートデータベース(ベンダーバージョンとローカルワークフロー)に取り組んでいます。このような多種多様なクエリのニーズを満たし、履歴データを処理するために、カバーインデックスのすべての組み合わせを作成することは不可能です。 2つの大きな(数百万から数千万の行)テーブルが内部結合されており、多数のフィールドを引っ張っていて、カバーするインデックスが存在しないため、内部クエリでサーバーのパフォーマンスが低下します。

もっとも最大の問題は、上の議論では現れていないようです。たぶんあなたのデータベースはきちんとしたデータを保証するためにトリガーとうまく設計されたトランザクション処理でうまく設計されています。鉱山は彼らが予想されないところでしばしばNULL値を持ちます。はい、テーブル定義はNullを強制できませんが、それは私の環境ではオプションではありません。

それで問題は...あなたがクエリをスピードのためだけに設計しているか、同じコードを毎分何千回も実行するトランザクション処理のためのより高い優先順位です。それとも、左外部結合が提供する正確さのために行きますか。内部結合は両側で一致を見つける必要があることを忘れないでください。そのため、予期しないNULLは2つのテーブルからデータを削除するだけでなく、情報の行全体を削除する可能性があります。そして、それはとてもうまく起こります、エラーメッセージなし。

必要なデータの90%を取得し、内部結合が黙って情報を削除したことを発見しないので、あなたは非常に速くなることができます。場合によっては内部結合がより高速になることがありますが、実行計画を確認していない限り、だれもがその仮定をしているとは思いません。速度は重要ですが、正確さはより重要です。

7
J.O.

パフォーマンス上の問題は、実行している結合の数、および結合している列にインデックスがあるかどうかに起因する可能性があります。

最悪の場合、結合ごとに9回のテーブル全体のスキャンを簡単に実行できます。

5
eddiegroves

外部結合をビューで使用すると優れたパフォーマンスが得られます。

ビューを含むクエリがあり、そのビューが10個のテーブルを結合して構成されているとします。 10個のテーブルのうち3個のカラムが使用されているとします。

これらの10個のテーブルが内部結合されている場合、クエリ自体が必要ないのに、クエリオプティマイザはそれらすべてを結合する必要があります。テーブルの10の。これは、内部結合自体がデータをフィルタリングし、計算に不可欠なものにする可能性があるためです。

それらの10個のテーブルが代わりに外部結合されている場合、クエリオプティマイザは実際には必要なものだけを結合します。この場合。これは、結合自体がデータをフィルタ処理しなくなったため、未使用の結合をスキップできるためです。

ソース: http://www.sqlservercentral.com/blogs/sql_coach/2010/07/29/poor-little-misunderstood-views/

4
MarredCheese

内部結合が左結合より速いかどうかをチェックするとき、私はSQLサーバーで何か面白いものを見つけました。

左結合テーブルの項目をselect文に含めないと、左結合は内部結合を使用した同じクエリよりも高速になります。

Select文に左結合テーブルを含めた場合、同じクエリを使用する内部結合は左結合と同じかそれより速くなりました。

1
Buzzzzzzz