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


259

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

1
から属性を投影しますcoUOMか?そうでない場合は、準結合を使用できる場合があります。はいの場合UNION、代替として使用できます。あなたのFROM条項だけを投稿することはここでは不十分な情報です。
onedaywhen

1
私はこれを非常に頻繁に疑問に思っていました(いつも目にしているからです)。
Paul Draper

1
簡単なスキーマでOrder Byを逃しましたか?最近、INNER JOINをLEFT OUTER JOINに変更すると、クエリが3分から10秒に高速化するという問題に直面しました。クエリに実際にOrder Byがある場合は、回答としてさらに説明します。すべての回答が私が直面したケースを実際に説明していないように見えました。
Phuah Yee Keat、2015

回答:


403

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

(そして、特定の状況では、因子の想像しにくい合流点によりa LEFT JOIN より高速であったとしても、機能的にはと同等ではないため、単に1つのインスタンスをすべて別のものに置き換えることはできません!)INNER JOIN

おそらく、パフォーマンスの問題は、候補キーや外部キーに適切にインデックスが付けられていないなど、他の場所にあります。9つのテーブルはかなりの数になるので、スローダウンは文字通りほとんどどこにでもあり得ます。スキーマを投稿すると、詳細を提供できる場合があります。


編集:

これをさらに振り返ると、a LEFT JOINがよりも速い状況が1つ考えられますINNER JOIN

  • 一部のテーブルは非常に小さい(たとえば、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です。前者はある通常はるかに高速が、行の数は非常に小さなであるから使用へのインデックスがありません、ハッシュ演算は、クエリの中で最も高価な部分であることが判明しました。

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

ただし、このクエリをID列ではなくに変更するNameと、まったく異なるストーリーが表示されます。その場合には、それは両方のクエリのネストされたループを行いますが、INNER JOINバージョンがして、クラスタ化インデックススキャンのいずれかを交換することができ求める-これは文字通りになることを意味の行数が多い速いです。

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


4
OUTER JOINがINNER JOINよりもパフォーマンスが高くなる可能性のある別のシナリオがあります。以下の私の答えを参照してください。
dbenham 2011

12
基本的に、内部結合と外部結合のパフォーマンスが異なるという考えをサポートするデータベースドキュメントがないことを指摘しておきます。データの量と結果セットのサイズのため、外部結合は内部結合よりも少し高価です。ただし、基になるアルゴリズム(msdn.microsoft.com/en-us/library/ms191426(v=sql.105).aspx)は、両方の種類の結合で同じです。同じ量のデータを返す場合、パフォーマンスは同様です。
ゴードンリノフ

3
@Aaronaught。。。この回答は、「外部結合は内部結合よりも大幅にパフォーマンスが悪い」という影響を与えるコメントで参照されました。この誤解が広がらないように、私はコメントしました。
ゴードンリノフ

16
この回答は1つの重要な側面で誤解を招くと思います。「左の結合は内部の結合よりも絶対に速くない」と述べているためです。この行は正しくありません。理論的には、INNER JOINよりも高速ではありません。「絶対に速くはない」というわけではありません。問題は特にパフォーマンスの問題です。実際には、INNER JOINがOUTER JOINに比べて途方もなく遅い、いくつかのシステム(非常に大規模な会社によるもの)を目にしました。理論と実践はまったく異なります。
David Frenkel 2013

5
@DavidFrenkel:それはほとんどあり得ない。そのような不一致が可能であると思われる場合は、実行計画とのA / B比較を確認してください。おそらくそれは、キャッシュされたクエリ/実行プラン、または不適切な統計に関連しています。
アーロンノート、2013

127

まだ説明されていない内部結合よりも外部結合の方が高速になる可能性のある重要なシナリオが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は計画からBを削除します。この場合、外部結合がSQL Serverの内部結合よりもどのように優れているかを簡単に確認できます。

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

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

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

最後に、上記の観点からパフォーマンスへの影響をテストしていませんが、理論的には、<FOREIGN_KEY> IS NOT NULL条件を追加すると、INNER JOINをOUTER JOINに安全に置き換えることができるはずです。 where句に。


5
非常に動的なクエリを作成するときに、実際にこの問題に遭遇しました。私が使用していてデータをプルしていなかったINNER JOINを残していたので、それをLEFT JOIN(せん断の好奇心から)に切り替えると、クエリの実行が実際より速くなりました。
エリックフィリップス

1
編集-オプティマイザが外部結合テーブルを実行プランから削除するために存在しなければならない条件を明確にしました。
dbenham 2012

2
回答に対する1つのマイナーな説明:外部キー列がnull可能でない場合、INNER JOINとLEFT JOINは意味的に同等になります(つまり、提案されたWHERE句は冗長です)。唯一の違いは実行計画です。
ダグラス

2
これは一見取るに足らない例を示していますが、これは非常に洞察に満ちた答えです!
pbalaga

6
+1:非常に大きなテーブルで内部結合を使用していたいくつかのクエリでこれに遭遇したようです。内部結合により、クエリプランのtempdbが流出しました(上記の理由から、サーバーにはすべてをメモリに保持するためのRAMが不足しています)。左結合に切り替えると、tempdbへのスピルがなくなり、結果として、20〜30秒のクエリの一部が1秒未満で実行されるようになりました。これは、ほとんどの人が内部結合の方が高速であるという全面的な仮定をしているように見えるため、非常に重要な問題です。
phosplait 2016

23

すべてが正常に機能しない場合でも、特にクエリオプティマイザー、クエリプランのキャッシュ、および統計に関しては、すべてが正常に機能しないことがわかっています。

最初に、インデックスと統計を再構築し、クエリプランのキャッシュをクリアして、混乱を招かないようにすることをお勧めします。しかし、それが終わっても問題を経験しました。

左結合が内部結合よりも高速であるいくつかのケースを経験しました。

根本的な理由は次のとおりです。2つのテーブルがあり、(両方のテーブルで)インデックス付きの列で結合する場合。内部結合は、テーブル1のインデックスのエントリをループして、テーブル2のインデックスと一致する場合と同じ結果を生成します。逆の場合と同様に、テーブル2のインデックスのエントリをループしてインデックスと一致します。表1で。問題は、誤解を招くような統計がある場合、クエリオプティマイザーはインデックスの統計を使用して、(他の基準に基づいて)一致するエントリが最も少ないテーブルを見つけます。それぞれ100万個のテーブルが2つある場合、テーブル1では10行が一致し、テーブル2では100000行が一致します。最善の方法は、テーブル1でインデックススキャンを実行し、テーブル2で10回一致させることです。逆は、100000行以上をループし、100000回一致させようとするインデックススキャンであり、10回しか成功しません。したがって、統計が正しくない場合、オプティマイザは間違ったテーブルとインデックスを選択してループする可能性があります。

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

ただし、オプティマイザは、左結合を左準結合として準最適化することもできます。必要なものを選択するには、強制順序ヒントを使用できます。


18

OPTION (FORCE ORDER)最後に両方のクエリ(内部結合と左結合のクエリ)を試し、結果を投稿します。OPTION (FORCE ORDER)は、クエリで指定した結合順序を使用してオプティマイザに実行プランを強制的に作成するクエリヒントです。

INNER JOINがと同じくらい速く動作し始める場合LEFT JOIN、それは次の理由によります。

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

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


9

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

ただし、最大の問題は、上記の議論では解決されないようです。おそらく、データベースは、適切なデータを確保するために、トリガーとトランザクション処理がうまく設計されています。鉱山には、予期しないNULL値が頻繁に含まれています。はい、テーブル定義は非ヌルを強制することができますが、私の環境ではそれはオプションではありません。

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

必要なデータの90%を取得し、内部結合が情報を静かに削除していないことを発見しないため、非常に高速になる可能性があります。内部結合の方が速い場合がありますが、実行計画を検討していない限り、誰もがそのようなことを想定しているとは思いません。速度は重要ですが、精度はより重要です。


8

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

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


7

外部結合をビューで使用すると、優れたパフォーマンスを提供できます。

たとえば、ビューを含むクエリがあり、そのビューは10個のテーブルを結合して構成されているとします。クエリで、これらの10個のテーブルのうち3個の列のみを使用するとします。

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

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

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


1
「外部結合」についてのあなたの声明は誤解を招きやすく、潜在的に不正確です。外部は、反対側のデータが存在する必要がないことを意味します-そしてそれがNULLを置換しない場合。特定の状況下では、RDBMSはそれらを「スキップ」する場合があります(dbenhamからの上記の回答を参照)。ただし、外部と内部では、クエリが根本的に異なる結果を返す可能性があります。INNERは、アイテムがAとBの両方にある結果を示します。LEFTOUTERは、すべてのAを意味し、オプションで、存在する場合はBを意味します。最初のケース-いくつかの行を取得し、2番目にすべての行を取得します。
ripvlan 2017年

1
@ripvlanもちろん、外部結合と内部結合は常に互換性があるとは限りません。元の質問はパフォーマンスに関するものでした。つまり、どちらの結合でも同じ結果セットが返される場合について話しているということです。
MarredCheese

1
はい。OUTERを使用すると、すべての行(より多くのデータ)が返されるため、パフォーマンスの問題が発生する可能性があります。クエリの結果が同じ出力になるというあなたの仮定は公正です-しかし、それは一般的なケースでは真実ではなく、各db設計に固有ではありません。また、リレーショナル代数に完全に精通していない人にとっては、悲しみを引き起こす可能性があります。私のポイントは、アドバイスを求めてこれを読んでいる人々により多くの洞察を提供することであり、左/右は魔法のように問題を解決せず、より多くの問題を引き起こす可能性があるということです。レベル300のパワーです:-)
ripvlan

2

内部結合が左結合よりも速いかどうかを確認したところ、SQLサーバーで興味深いことがわかりました。

左側の結合テーブルの項目を含めない場合、selectステートメントでは、左側の結合は内部結合を使用する同じクエリよりも高速になります。

selectステートメントに左結合テーブルを含めた場合、同じクエリを使用した内部結合は、左結合と同じかそれよりも高速でした。


0

私の比較から、それらはまったく同じ実行プランを持っていることがわかります。3つのシナリオがあります。

  1. それらが同じ結果を返す場合、それらは同じ速度になります。ただし、これらは同じクエリではなく、LEFT JOINはより多くの結果を返す可能性があることを覚えておく必要があります(一部のON条件が満たされない場合)---これが通常遅い理由です。

  2. メインテーブル(実行プランの最初の非constテーブル)に制限条件(WHERE id =?)があり、対応するON条件がNULL値である場合、「正しい」テーブルは結合されません---これは、 LEFT JOINの方が高速です。

  3. ポイント1で説明したように、通常、INNER JOINは制限が厳しく、返される結果が少ないため、高速です。

どちらも(同じ)インデックスを使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.