web-dev-qa-db-ja.com

JOINとWHEREでのクエリのフィルタリングの違いは?

SQLでは、IDに基づいて結果をフィルタリングしようとしていますが、間に論理的な違いがあるかどうか疑問に思っています

SELECT value 
FROM table1 
JOIN table2 ON table1.id = table2.id 
WHERE table1.id = 1

そして

SELECT value 
FROM table1 
JOIN table2 ON table1.id = table2.id AND table1.id = 1

私には、常に同じ結果のセットが得られますが、ロジックが異なるように見えますが、2つの異なる結果セットが得られるような条件があるのか​​(または常にまったく同じ2つの結果セットが返されるのか) )

29
warsong

答えは[〜#〜] no [〜#〜]違いですが、次のとおりです。

私はいつも次のことをすることを好みます。

  • 結合条件ON句に常に保持する
  • 常にfilter'swhere句に入れます

これにより、クエリが読みやすくなります。

したがって、このクエリを使用します。

SELECT value
FROM table1
INNER JOIN table2
        ON table1.id = table2.id
WHERE table1.id = 1

ただし、OUTER JOIN'Sを使用している場合、ON条件とWhere条件でフィルターを維持することには大きな違いがあります。

論理クエリ処理

次のリストには、クエリの一般的な形式と、さまざまな句が論理的に処理される順序に従って割り当てられたステップ番号が含まれています。

(5) SELECT (5-2) DISTINCT (5-3) TOP(<top_specification>) (5-1) <select_list>
(1) FROM (1-J) <left_table> <join_type> JOIN <right_table> ON <on_predicate>
| (1-A) <left_table> <apply_type> APPLY <right_table_expression> AS <alias>
| (1-P) <left_table> PIVOT(<pivot_specification>) AS <alias>
| (1-U) <left_table> UNPIVOT(<unpivot_specification>) AS <alias>
(2) WHERE <where_predicate>
(3) GROUP BY <group_by_specification>
(4) HAVING <having_predicate>
(6) ORDER BY <order_by_list>;

フロー図の論理クエリ処理

Enter image description here

  • (1)FROM:FROMフェーズでは、クエリのソーステーブルを識別し、テーブル演算子を処理します。各テーブル演算子は一連のサブフェーズを適用します。たとえば、結合に含まれるフェーズは、(1-J1)デカルト積、(1-J2)ONフィルター、(1-J3)外部列の追加です。 FROMフェーズでは、仮想テーブルVT1が生成されます。

  • (1-J1)デカルト積:このフェーズでは、テーブル演算子に含まれる2つのテーブル間でデカルト積(クロス結合)を実行し、VT1-J1を生成します。

  • (1-J2)ON Filter:このフェーズでは、ON句に現れる述語(<on_predicate>)に基づいてVT1-J1からの行をフィルタリングします。述語がTRUEと評価される行のみがVT1-J2に挿入されます。
  • (1-J3)Add Outer Rows:OUTER JOINが指定されている場合(CROSS JOINまたはINNER JOINではなく)、保存されているテーブルまたはテーブルの行一致が見つからなかった場合は、VT1-J2の行に外部行として追加され、VT1-J3が生成されます。
  • (2)[〜#〜] where [〜#〜]:このフェーズでは、WHERE句に現れる述語に基づいてVT1からの行をフィルタリングします()。述語がTRUEと評価された行のみがVT2に挿入されます。
  • (3)GROUP BY:このフェーズでは、GROUP BY句で指定された列リストに基づいて、VT2からの行をグループに配置し、VT3を生成します。最終的に、グループごとに1つの結果行があります。
  • (4)HAVING:このフェーズでは、HAVING句に現れる述語(<having_predicate>)に基づいて、VT3からグループをフィルタリングします。述語がTRUEと評価されたグループのみがVT4に挿入されます。
  • (5)SELECT:このフェーズでは、SELECT句の要素を処理し、VT5を生成します。
  • (5-1)式の評価:このフェーズでは、SELECTリストの式を評価し、VT5-1を生成します。
  • (5-2)DISTINCT:このフェーズでは、VT5-1から重複行を削除し、VT5-2を生成します。
  • (5-3)TOP:このフェーズでは、ORDER BY句で定義された論理順序に基づいて、VT5-2から指定された上位数または行のパーセンテージをフィルタリングし、テーブルVT5-3を生成します。
  • (6)ORDER BY:このフェーズでは、ORDER BY句で指定された列リストに従ってVT5-3から行をソートし、カーソルVC6を生成します。

この優れたリンクから参照されます。

39

VR46が指摘したように、INNER JOINSを使用しても違いはありませんが、OUTER JOINSを使用して2番目のテーブルの値を評価すると(左結合の場合)大きな違いがあります。 -右結合の最初のテーブル)。次の設定を検討してください。

DECLARE @Table1 TABLE ([ID] int)
DECLARE @Table2 TABLE ([Table1ID] int, [Value] varchar(50))

INSERT INTO @Table1
VALUES
(1),
(2),
(3)

INSERT INTO @Table2
VALUES
(1, 'test'),
(1, 'hello'),
(2, 'goodbye')

左外部結合を使用してそこから選択し、where句に条件を配置すると、次のようになります。

SELECT * FROM @Table1 T1
LEFT OUTER JOIN @Table2 T2
    ON T1.ID = T2.Table1ID
WHERE T2.Table1ID = 1

次の結果が得られます。

ID          Table1ID    Value
----------- ----------- --------------------------------------------------
1           1           test
1           1           hello

これは、where句が結果セットを制限するため、IDが1のtable1のレコードのみが含まれるためです。ただし、条件をon句に移動すると、次のようになります。

SELECT * FROM @Table1 T1
LEFT OUTER JOIN @Table2 T2
    ON T1.ID = T2.Table1ID
    AND T2.Table1ID = 1

次の結果が得られます。

ID          Table1ID    Value
----------- ----------- --------------------------------------------------
1           1           test
1           1           hello
2           NULL        NULL
3           NULL        NULL

これは、table1のID 1によって結果セットをフィルターするのではなく、JOINをフィルターするためです。したがって、2番目のテーブルでtable1のID 2が一致しても、それは結合から除外されますが、結果セット(したがってnull値)からは除外されません。

したがって、内部結合の場合は重要ではありませんが、読みやすさと一貫性を保つためにwhere句に保持する必要があります。ただし、外部結合の場合、結果セットに影響を与えるため、条件をどこに置くかが問題になることに注意する必要があります。

12
Stan Shaw

「正しい」と答えたのは正しくないと思います。どうして?説明してみます:

意見があります

「常にON句に結合条件を保持する常にフィルタをwhere句に配置する」

そして、これは間違っています。内部結合を使用している場合は、フィルターパラメーターを毎回ON句に配置します。場所には配置しません。なぜですか?複雑なWHERE句(たとえば、関数や計算の使用)を使用した、合計10個のテーブル(つまり、すべてのテーブルに10kのレコードがある)結合の複雑なクエリを想像してみてください。 ON句にフィルタリング条件を指定すると、これらの10個のテーブル間のJOINSは発生せず、WHERE句はまったく実行されません。この場合、WHERE句で10000 ^ 10の計算を実行していません。これは理にかなっており、WHERE句のみにフィルタリングパラメータを配置することはできません。

3
Juozas