INNER JOIN ONとWHERE句


941

簡単にするために、すべての関連フィールドがであると仮定しますNOT NULL

できるよ:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1, table2
WHERE
    table1.foreignkey = table2.primarykey
    AND (some other conditions)

または他の場合:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1 INNER JOIN table2
    ON table1.foreignkey = table2.primarykey
WHERE
    (some other conditions)

これら2つは同じ方法で機能しMySQLますか?




18
私が正しく理解していれば、最初のバリアントはANSI SQL-89の暗黙の構文で、2番目のバリアントはANSI SQL-92の明示的な結合構文です。両方とも、SQL実装の適合では同じ結果になり、適切に行われたSQL実装では両方とも同じクエリプランになります。私は個人的にSQL-89構文を好みますが、多くの人々はSQL-92構文を好みます。
ミッコランタライネン2016

11
@Hogan異なる構文の正式名称を指摘していた。完全な名前を明記した回答はなかったので、コメントとして追加することにしました。しかし、私のコメントは実際の質問には回答しなかったため、回答としてではなくコメントとして追加しました。(投票数の多い回答には、「INNER JOIN is ANSI syntax」や「暗黙の結合ANSI構文は古い」などの主張があり、どちらの構文も異なるANSI構文であるため、何も言われません。)
Mikko Rantalainen

回答:


710

INNER JOIN 使用すべきANSI構文です。

特に多くのテーブルを結合する場合は、一般的に読みやすくなります。

またOUTER JOIN、必要なときにいつでも簡単に置き換えることができます。

WHERE構文は、より多くのリレーショナルモデルが向いています。

2つのテーブルJOINedの結果は、結合列が一致する行のみを選択するフィルターが適用されるテーブルのデカルト積です。

これをWHERE構文で見ると簡単です。

あなたの例については、MySQL(および一般にSQL)では、これら2つのクエリはシノニムです。

MySQLにもSTRAIGHT_JOIN句があることに注意してください。

この句を使用JOINすると、外部ループでスキャンされるテーブルと内部ループでスキャンされるテーブルの順序を制御できます。

MySQLでは、WHERE構文を使用してこれを制御することはできません。


10
ありがとう、Quasnoi。あなたのansにはたくさんの詳細があります。「そうです、これらのクエリは同等ですが、読みやすく、変更が簡単なため、内部結合を使用する必要があります」と言ってもよいでしょうか。
allyourcode 2009

8
@allyourcode:についてOracleSQL ServerMySQLおよびPostgreSQL-はい。他のシステムの場合も、おそらくそうですが、確認することをお勧めします。
Quassnoi 2009

13
FWIW、WHERE句で結合条件とコンマを使用することもANSI標準にあります。
Bill Karwin、2010年

1
@Bill KarwinJOINキーワードは、最近のように見えるまで、独自仕様の一部ではありませんでした。それはにその方法を作ったOracle唯一のバージョンで9とにPostgreSQLバージョンで7.2(両方ともにリリース2001)。このキーワードの出現はANSI標準的な採用の一部であり、そのためANSI、後者は同義語としてカンマもサポートしているにもかかわらず、このキーワードは通常に関連付けられてCROSS JOINいます。
Quassnoi、2010年

9
それでも、ANSI SQL-89は、WHERE句のコンマと条件を使用して結合を指定しました(条件なしでは、結合は、クロス結合と同じです)。ANSI SQL-92はJOINキーワードと関連する構文を追加しましたが、下位互換性のためにカンマスタイルの構文が引き続きサポートされています。
Bill Karwin、2010年

182

他のINNER JOIN人は人間の読みやすさに役立つと指摘しており、それが最優先事項であると私は同意します。結合構文がより読みやすい理由
を説明してみましょう。

基本的なSELECTクエリは次のとおりです。

SELECT stuff
FROM tables
WHERE conditions

このSELECT条項は、を取り戻しているかを示しています。FROM句が教えてくれるところ我々はからそれを取得している、とWHERE句が教えてくれるている私たちが取得しているもの。

JOIN テーブル、それらがどのように結合されているか(概念的には、実際には単一のテーブルに)についてのステートメントです。

テーブルを制御するすべてのクエリ要素(データの取得元)は、意味的にはFROM句に属します(もちろん、JOIN要素が移動する場所です)。joining-elementsをWHERE句に入れると、whichwhere-fromが統合されるため、JOIN構文が推奨されます。


7
カールが内部結合を優先する理由を明確にしていただきありがとうございます。あなたのansは他のものでは暗黙的だったと思いますが、通常は明示的な方がいいです(そうです、私はPythonのファンです)。
allyourcode 2009

2
ONとWHEREのセマンティクスは、最後のOUTER JOINの後のJOINの場合、どちらを使用しても問題ないことを意味します。ONはJOINの一部として特徴付けられますが、デカルト積の後のフィルタリングであります。ONとWHEREの両方でデカルト積をフィルタリングします。ただし、最後のOUTER JOINの前に ONまたはWHEREを指定した副選択を使用する必要があります。(JOINは「オン」の列ペアではありません。任意の2つのテーブルを任意の条件でJOINできます。これは、JOIN ON ONの列の等価性を具体的に解釈するための単なる方法です。)
philipxy

WHEREを使用してINNER JOINと同じ結果を得る場合でも、クエリのFROM部分で2つのテーブルに言及します。したがって、基本的にはまだFROM句でデータを取得している場所を示唆しているので、必ずしも「theとwhere-fromを
照合

@ArsenKhachaturyanキーワードまたは識別子がテキストで使用されているからといって、それがコードであり、コード形式が必要であるとは限りません。これはどのような形式の選択でもかまいません。ここで編集するのが妥当であれば、すべての投稿を常に他の形式に編集することは正当化できます。つまり、正当化できません。(さらに、インラインのワードごとのコード形式は読みにくい場合があります。)ここの段落の区切りについても同じです-特に明確ではありません。「どちら」か「それ」でも同じです。また、プログラミング言語の名前はコード形式であってなりませ。PSあなたはエラーで改行を追加しました。
philipxy

@philipxyは「それは意味しない...」と述べましたが、明らかにそれはコードキーワードでマークすることができないことを意味しませんでした。はい、それは作られる選択ですが、多くの投稿はその事実を知らずに行われます。したがって、変更を行うという私の決定は、何かを壊すことを意図したものではなく、より読みやすくすることを目的としています。変更をフォーマットした後に中断に気付いた場合は、申し訳ありませんが、そのような変更を元に戻すことができます。
Arsen Khachaturyan

143

ON / WHEREでの条件ステートメントの適用

ここでは、論理クエリの処理手順について説明しました。


参照:Microsoft®SQL Server™2005 T-SQLクエリの
発行元:Microsoft Press
Pub日付:2006年3月7日
印刷ISBN-10:0-7356-2313-9
印刷ISBN-13:978-0-7356-2313-2
ページ:640

Microsoft®SQL Server™2005 T-SQLクエリの内部

(8)  SELECT (9) DISTINCT (11) TOP <top_specification> <select_list>
(1)  FROM <left_table>
(3)       <join_type> JOIN <right_table>
(2)       ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>

他のプログラミング言語とは異なるSQLの最初の注目すべき側面は、コードが処理される順序です。ほとんどのプログラミング言語では、コードは記述された順序で処理されます。SQLでは、処理される最初の句はFROM句ですが、最初に現れるSELECT句はほぼ最後に処理されます。

各ステップは、次のステップへの入力として使用される仮想テーブルを生成します。これらの仮想テーブルは、呼び出し元(クライアントアプリケーションまたは外部クエリ)では使用できません。最後のステップで生成されたテーブルのみが呼び出し元に返されます。クエリで特定の句が指定されていない場合、対応するステップは単にスキップされます。

論理クエリ処理フェーズの簡単な説明

ステップの説明が今のところあまり意味をなさないように見えても、あまり心配しないでください。これらはリファレンスとして提供されています。シナリオの例の後のセクションでは、手順についてさらに詳しく説明します。

  1. FROM:デカルト積(クロス結合)がFROM句の最初の2つのテーブル間で実行され、その結果、仮想テーブルVT1が生成されます。

  2. ON:ONフィルターがVT1に適用されます。<join_condition>がTRUE である行のみがVT2に挿入されます。

  3. OUTER(結合):(CROSS JOINまたはINNER JOINではなく)OUTER JOINが指定されている場合、一致が見つからなかった1つまたは複数の保持されたテーブルの行が、VT2の行に外部行として追加され、生成されますVT3。FROM句に3つ以上のテーブルが表示される場合、すべてのテーブルが処理されるまで、最後の結合の結果とFROM句の次のテーブルの間にステップ1〜3が繰り返し適用されます。

  4. WHERE:WHEREフィルターはVT3に適用されます。<where_condition>がTRUE である行のみがVT4に挿入されます。

  5. GROUP BY:VT4からの行は、GROUP BY句で指定された列リストに基づいてグループに配置されます。VT5が生成されます。

  6. CUBE | ROLLUP:スーパーグループ(グループのグループ)がVT5からの行に追加され、VT6が生成されます。

  7. HAVING:HAVINGフィルターはVT6に適用されます。<having_condition>がTRUE であるグループのみがVT7に挿入されます。

  8. SELECT:SELECTリストが処理され、VT8が生成されます。

  9. DISTINCT:重複する行がVT8から削除されます。VT9が生成されます。

  10. ORDER BY:VT9からの行は、ORDER BY句で指定された列リストに従ってソートされます。カーソルが生成されます(VC10)。

  11. TOP:指定した行数または割合の行がVC10の最初から選択されます。テーブルVT11が生成され、呼び出し元に返されます。



したがって、(INNER JOIN)ONは、WHERE句を適用する前にデータをフィルター処理します(VTのデータカウントはここで削減されます)。後続の結合条件は、パフォーマンスを向上させるフィルター処理されたデータで実行されます。その後、WHERE条件のみがフィルター条件を適用します。

(ON / WHEREで条件ステートメントを適用しても、ほとんどの場合大きな違いはありません。これは、結合したテーブルの数と各結合テーブルで使用可能な行の数によって異なります)


10
「そのため、(INNER JOIN)ONは、WHERE句を適用する前にデータをフィルタリングします(VTのデータカウントはここで削減されます)。」必ずしも。この記事は、処理の論理的な順序に関するものです。特定の実装が別のことより先に行うと言う場合、実装された処理の順序について話していることになります。結果が実装が論理的な順序に従った場合と同じである限り、実装は好みの最適化を行うことができます。Joe CelkoはこれについてUsenetでたくさん書いています。
Mike Sherrill 'Cat Recall'

@rafidheen "(INNER JOIN)ONはデータをフィルタリングします... WHERE句を適用する前に...パフォーマンスを向上させます。" いい視点ね。「その後はWHERE条件のみがフィルター条件を適用します」HAVING句についてはどうですか?
ジェームズ

@ジェームスrafidheenによる主張は間違っています。マニュアルの「結合の最適化」を参照してください。このページに関する他のコメントも。(そしてMikeSherrill'CatRecallの例です。)このような「論理的」な説明は、実際の計算方法ではなく、結果の値を説明しています。また、そのような実装動作が変更されないことが保証されていません。
philipxy

67

暗黙の結合ANSI構文は古く、あまり明確ではなく、推奨されません。

さらに、リレーショナル代数を使用すると、WHERE句との述語を交換できるINNER JOINため、句を使用したINNER JOINクエリでもWHERE、オプティマイザによって述語を再配置できます。

できるだけ読みやすい方法でクエリを記述することをお勧めします。

これには、INNER JOIN比較的「不完全」なものをWHERE作成し、フィルター基準のリストをより簡単に保守できるようにするために、いくつかの基準を単純に含めることが含まれる場合があります。

たとえば、次の代わりに:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
    AND c.State = 'NY'
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
    AND a.Status = 1

書く:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
WHERE c.State = 'NY'
    AND a.Status = 1

しかし、それはもちろん異なります。


16
最初のスニペットは間違いなく私の脳をもっと傷つけます。誰かが実際にそれをしますか?それをする人に会ったら、頭を倒しても大丈夫ですか?
allyourcode 2009

3
私はそれが最も理にかなっている基準を見つけます。時間的に整合性のあるスナップショットルックアップテーブルに結合している場合(および有効な日付の選択を強制するビューまたはUDFがない場合)、有効日はWHEREではなく結合に含めます。誤って削除される可能性があります。
Cade Roux、

14
@allyourcode:このタイプの結合構文がINNER JOINで表示されることはまれですが、RIGHT JOINとLEFT JOINSでよく見られます-結合述語で詳細を指定すると、サブクエリが不要になり、外部結合が誤ってオンになるのを防ぎますINNER JOINに。(INNER JOINの場合、ほとんど常にc.State = 'NY'をWHERE句に挿入することに同意します)
Dave Markle

1
@allyourcode私は間違いなくそうします!そして、私はケイドに同意します。適切でない理由
知り

31

暗黙的な結合(最初のクエリの名前)は、クエリにテーブルを追加する必要があると、はるかに混乱し、読みにくく、保守が難しくなります。4つまたは5つの異なるテーブルで同じクエリとタイプの結合を行うことを想像してみてください...それは悪夢です。

明示的な結合(2番目の例)を使用すると、はるかに読みやすく、保守が容易になります。


48
私はこれ以上反対することができませんでした。JOIN構文は非常に冗長で、整理するのが困難です。WHERE句結合を使用して5、10、15のテーブルを結合するクエリがたくさんあり、完全に読み取り可能です。JOIN構文を使用してそのようなクエリを書き換えると、文字化けが発生します。これは、この質問に対する正しい答えがないことと、それがより快適に何に依存するかを示しています。
ノアイェッター2006年

33
ノア、あなたはここに少数派かもしれないと思います。
matt b

2
マットとノアに+1します。私は多様性が好きです:) 私はノアがどこから来ているかを見ることができます。内部結合は言語に新しいものを追加するものではなく、間違いなくより冗長です。一方、「where」条件を大幅に短縮できるため、通常は読みやすくなります。
allyourcode 2009

5
適切なDBMSであれば、2つのクエリを同じ実行プランに変換すると思います。ただし、実際には各DBMSは異なり、確実に知る唯一の方法は実際に実行プランを調べることです(つまり、自分でテストする必要があります)。
matt b

@rafidheenが別の回答(SQL実行の詳細なシーケンスを持つもの)で提案されているように、JOINは一度に1つずつフィルタリングされ、3つ以上のテーブルの完全なデカルト結合と比較した場合に結合操作のサイズが削減されることは本当ですか? WHEREフィルターがさかのぼって適用されていますか?もしそうなら、それはJOINがパフォーマンスの改善を提供することを示唆します(また別の答えで指摘されているように、左/右結合の利点も)。
ジェームズ

26

また、古い構文を使用するとエラーが発生しやすくなることも指摘しておきます。ON句なしで内部結合を使用すると、構文エラーが発生します。古い構文を使用していて、where句の結合条件の1つを忘れた場合は、クロス結合になります。開発者は、(結合自体が壊れていることにまだ気付いていないために結合を修正するのではなく)キーワードを追加してこれを修正しますが、これは問題を解決するように見えますが、クエリをかなり遅くします。

さらに、古い構文にクロスジョインがある場合のメンテナンスについて、メンテナは、クロスジョインが必要な場合(クロスジョインが必要な状況がある場合)または修正する必要のある事故であるかどうかをどのようにして知るのでしょうか。

左結合を使用する場合に暗黙の構文が悪い理由を確認するために、この質問を紹介します。 Sybase * =同じ内部テーブルに対して2つの異なる外部テーブルを使用するAnsi標準

さらに(ここでは個人的な暴言)、明示的な結合を使用する標準は20年以上前のものです。つまり、暗黙的な結合構文はその20年で古くなっています。20年以上古い構文を使用してアプリケーションコードを記述しますか?なぜ、データベースコードを記述したいのですか?


3
@HLGEM:明示的なJOINの方が良いことに完全に同意しますが、古い構文を使用する必要がある場合もあります。実際の例:ANSI JOINは、2001年にリリースされたバージョン9iでのみOracleに組み込まれ、わずか1年前(規格が公開されてから16年)まで、私たちが持っていた一連の8iインストールをサポートする必要がありました。重要なアップデートをリリースします。2セットの更新を維持したくなかったので、8iを含むすべてのデータベースに対して更新を開発およびテストしたため、ANSI JOINを使用できませんでした。
Quassnoi 2009年

INNER JOINなしの構文はエラーが発生しやすいことを指摘すると、興味深い点が1つ増えます。「...明示的な結合を使用した標準は17歳です」と言ったとき、私はあなたの最後の文について混乱しています。それでは、INNER JOINキーワードを使用することを提案していますか?
Marco Demaio、2011

1
@Marco Demaio、はい、常にINNER JOINまたはJOIN(これら2つは同じです)またはLEFT JOINまたはRIGHT JOINまたはCROSS JOINを使用し、暗黙的なコンマ結合は使用しません。
HLGEM 2011

2
「なぜ[20歳]のデータベースコードを書きたいのですか?」-SQLが派生テーブルのサポートを開始してから「古い」SQLを使用して記述していることに気づきましたHAVING。また、「古くなった」NATURAL JOINと主張したとしても、使用しないことに気づきましたINNER JOIN。はい、あなたにはあなたの理由があります(ここで再度述べる必要はありません!):私の要点は、古い構文を使用するのが好きな人にも理由があり、構文の相対的な経年はほとんど関係がないということです。
いつか

1
WHEREはまだ標準にあります(そうでない場合は教えてください)。だから、明らかに時代遅れのものは何もない。また、「結合を修正するのではなく」ということは、一般的にDBMSから遠く離れた開発者を示しています。
ユルゲンA.エアハルト2017

12

人間が読める意味は異なります。

ただし、クエリオプティマイザーによっては、マシンにとって同じ意味を持つ場合があります。

常に読みやすいようにコーディングする必要があります。

つまり、これが組み込みの関係である場合は、明示的な結合を使用します。弱い関連データで照合する場合は、where句を使用します。


11

SQL:2003標準では、いくつかの優先ルールが変更されたため、JOINステートメントは「カンマ」結合よりも優先されます。これにより、実際の設定に応じてクエリの結果が変わる可能性があります。これは、MySQL 5.0.12が標準に準拠するように切り替えたときに、一部の人々にいくつかの問題を引き起こします。

したがって、あなたの例では、クエリは同じように機能します。しかし、3番目のテーブルを追加した場合:SELECT ... FROM table1、table2 JOIN table3 ON ... WHERE ...

MySQL 5.0.12より前は、table1とtable2が最初に結合され、次にtable3が結合されていました。これで(5.0.12以降)、table2とtable3が最初に結合され、次にtable1が結合されます。結果は常に変化するとは限りませんが、変化する可能性があり、気付かない場合もあります。

「カンマ」構文はもう使用しません。2番目の例を選択します。とにかく読みやすく、JOIN条件はJOINであり、個別のクエリセクションに分割されていません。


標準SQLは変更されていません。MySQLは間違っていましたが、今は正しいです。MySQLのマニュアルを参照してください。
philipxy

4

MySQLについて話しているのは知っていますが、とにかくOracle 9では、明示的結合と暗黙的結合で異なる実行プランが生成されます。Oracle 10+で解決されたAFAIK:そのような違いはもうありません。


1

ANSI結合構文は間違いなく移植性が高いです。

Microsoft SQL Serverのアップグレードを行っています。また、SQL Serverの外部結合の= *および* =構文は、2005 SQL Server以降ではサポートされていません(互換モードなし)。


2
SQL Server 2000でも、=と=は誤った結果をもたらす可能性があるため、使用しないでください。
HLGEM 2009年

2
*=および=*ANSIことはありませんでしたし、良い記法ことはありませんでした。そのため、ONが必要でした
サブセレクト

1

動的ストアドプロシージャを頻繁にプログラミングしている場合は、2番目の例(whereを使用)に夢中になります。さまざまな入力パラメーターと多くのモーフ混乱がある場合、それが唯一の方法です。それ以外の場合は、どちらも同じクエリプランを実行するため、従来のクエリに明らかな違いはありません。

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