それを読んだ後、これはnotExplicit vs Implicit SQL Joins の複製です。答えは関係しているかもしれませんが(あるいは同じでさえも)、 question は異なります。
違いは何ですか?それぞれに何を入れるべきですか?
理論を正しく理解すれば、クエリオプティマイザは両方を同じ意味で使用できるはずです。
それらは同じものではありません。
これらの質問を考えなさい:
SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID
WHERE Orders.ID = 12345
そして
SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID
AND Orders.ID = 12345
最初のものは、もしあれば、注文番号12345
の注文とその行を返します。 2番目のものはすべての注文を返しますが、注文12345
だけがそれに関連する行を持ちます。
INNER JOIN
の場合、句は 事実上 と同等です。ただし、機能的に同じであるという理由だけで、同じ結果が得られるという意味で、2種類の句が同じ意味を持つわけではありません。
外部結合に関する事項
a。 WHERE
句: - の後 結合。結合が行われた後、レコードはフィルタリングされます。
b。 ON
句 - 前 結合。 (右の表からの)レコードは、参加する前にフィルタリングされます。これは結果としてnullになる可能性があります(OUTER join以降)。
例 :以下の表を見てください。
1. documents:
| id | name |
--------|-------------|
| 1 | Document1 |
| 2 | Document2 |
| 3 | Document3 |
| 4 | Document4 |
| 5 | Document5 |
2. downloads:
| id | document_id | username |
|------|---------------|----------|
| 1 | 1 | sandeep |
| 2 | 1 | simi |
| 3 | 2 | sandeep |
| 4 | 2 | reya |
| 5 | 3 | simi |
a)WHERE
句の内側:
SELECT documents.name, downloads.id
FROM documents
LEFT OUTER JOIN downloads
ON documents.id = downloads.document_id
WHERE username = 'sandeep'
For above query the intermediate join table will look like this.
| id(from documents) | name | id (from downloads) | document_id | username |
|--------------------|--------------|---------------------|-------------|----------|
| 1 | Document1 | 1 | 1 | sandeep |
| 1 | Document1 | 2 | 1 | simi |
| 2 | Document2 | 3 | 2 | sandeep |
| 2 | Document2 | 4 | 2 | reya |
| 3 | Document3 | 5 | 3 | simi |
| 4 | Document4 | NULL | NULL | NULL |
| 5 | Document5 | NULL | NULL | NULL |
After applying the `WHERE` clause and selecting the listed attributes, the result will be:
| name | id |
|--------------|----|
| Document1 | 1 |
| Document2 | 3 |
b)JOIN
句の内側
SELECT documents.name, downloads.id
FROM documents
LEFT OUTER JOIN downloads
ON documents.id = downloads.document_id
AND username = 'sandeep'
For above query the intermediate join table will look like this.
| id(from documents) | name | id (from downloads) | document_id | username |
|--------------------|--------------|---------------------|-------------|----------|
| 1 | Document1 | 1 | 1 | sandeep |
| 2 | Document2 | 3 | 2 | sandeep |
| 3 | Document3 | NULL | NULL | NULL |
| 4 | Document4 | NULL | NULL | NULL |
| 5 | Document5 | NULL | NULL | NULL |
Notice how the rows in `documents` that did not match both the conditions are populated with `NULL` values.
After Selecting the listed attributes, the result will be:
| name | id |
|------------|------|
| Document1 | 1 |
| Document2 | 3 |
| Document3 | NULL |
| Document4 | NULL |
| Document5 | NULL |
INNER JOIN
s上でそれらは交換可能であり、オプティマイザはそれらを自由に再配置します。
OUTER JOIN
sでは、結合のどちら側に依存しているかによって、それらは必ずしも交換可能ではありません。
読みやすさに応じてどちらかに配置します。
やり方は次のとおりです。
INNER JOIN
を実行している場合は、結合条件を常にON
節に入れてください。そのため、ON句にWHERE条件を追加しないで、それらをWHERE
句に入れてください。
LEFT JOIN
を実行している場合は、結合の right 側にあるテーブルのON
句にWHERE条件を追加します。結合の右側を参照するWHERE句を追加すると、結合がINNER JOINに変換されるため、これは必須です。
例外は、特定のテーブルにないレコードを探しているときです。このようにして、RIGHT JOINテーブルの一意の識別子(これはNULLではない)への参照をWHERE句に追加します。WHERE t2.idfield IS NULL
。そのため、結合の右側にあるテーブルを参照する必要があるのは、そのテーブルにないレコードを見つけるときだけです。
この記事 は明らかに違いを説明しています。また、「ON join_conditionとWHERE join_conditionまたはjoint_aliasがnull」との説明もあります。
WHERE句はFROM句の結果をJOINとともにフィルタリングし、ON句はFROMテーブルとJOINテーブルの間のテーブル結果を生成するために使用されます。
where句 と on句 の間には大きな違いがあります。
これが例です。
mysql> desc t1;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
| fid | int(11) | NO | | NULL | |
| v | varchar(20) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
テーブルt2のidがあります。
mysql> desc t2;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | NO | | NULL | |
| v | varchar(10) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
"on句"で問い合わせます:
mysql> SELECT * FROM `t1` left join t2 on fid = t2.id AND t1.v = 'K'
-> ;
+----+-----+---+------+------+
| id | fid | v | id | v |
+----+-----+---+------+------+
| 1 | 1 | H | NULL | NULL |
| 2 | 1 | B | NULL | NULL |
| 3 | 2 | H | NULL | NULL |
| 4 | 7 | K | NULL | NULL |
| 5 | 5 | L | NULL | NULL |
+----+-----+---+------+------+
5 rows in set (0.00 sec)
"where句"で問い合わせます:
mysql> SELECT * FROM `t1` left join t2 on fid = t2.id where t1.v = 'K';
+----+-----+---+------+------+
| id | fid | v | id | v |
+----+-----+---+------+------+
| 4 | 7 | K | NULL | NULL |
+----+-----+---+------+------+
1 row in set (0.00 sec)
最初のクエリがt1からレコードを返し、t2からその従属行があればそれを返します(t1.v = 'K'がある場合)。
2番目のクエリはt1から行を返しますが、t1.v = 'K'の場合のみ、関連付けられた行が含まれます。
オプティマイザの観点からは、結合句をONで定義してもWHEREで定義しても違いはありません。
しかし、私見、結合を実行するときにON句を使用する方がはるかに明確だと思います。このようにして、結合がどのように処理されるのか、それ以外のWHERE句と混在するのかを指定する、クエリの特定のセクションがあります。
パフォーマンスを向上させるために、テーブルはJOINSに使用する特別なインデックスカラムを持つべきです。
だからあなたが条件付けたカラムがそれらのインデックスカラムの一つでないならば、私はそれをWHEREにそれを保存するのが良いと思います。
そのため、インデックス付き列を使用して結合し、結合後にインデックスなしの列に対して条件を実行します。
データを結合しようとしていますか、それともデータをフィルタしようとしていますか?
読みやすくするために、これらのユースケースをそれぞれONとWHEREに分離することが最も理にかなっています。
WHERE句にJOIN条件とフィルタ条件が存在するクエリを読むのは非常に困難になります。
パフォーマンスの面では違いはありませんが、SQLの種類によってクエリプランニングの扱いが異なる場合があります。したがって、¯\_(ツ)_/¯
を試してみる価値があります(クエリ速度に影響するキャッシュに注意してください)。
また、他の人が指摘したように、外部結合を使用した場合、ON句にフィルタ条件を指定した場合、結果が異なるのは1つのテーブルにのみ影響するためです。
私はここでこれについてより詳細な投稿を書きました: https://dataschool.com/learn/difference-between-where-and-on-in-sql
SQLでは、「WHERE」句と「ON」句は条件付きステートメントの一種ですが、両者の主な違いは、「条件」を指定するためにSelect/Updateステートメントで「Where」句が使用されるのに対して結合で使用され、テーブルが結合される前に、レコードがターゲットテーブルとソーステーブルで一致するかどうかを検証または確認します。
例: - 'WHERE'
SELECT * FROM employee WHERE employee_id=101
例: - 'ON'
employeeとemployee_detailsの2つのテーブルがあり、一致する列はemployee_idです。
SELECT * FROM employee
INNER JOIN employee_details
ON employee.employee_id = employee_details.employee_id
あなたの質問に答えてください。説明を元に戻します。
通常、フィルタリングは2つのテーブルが結合された後にWHERE句で処理されます。結合する前に、一方または両方のテーブルをフィルタ処理することをお勧めします。つまり、where句は結果セット全体に適用されますが、on句は問題の結合にのみ適用されます。
内部結合の場合は、WHERE
とON
を同じ意味で使用できます。実際、相関副照会でON
を使用することは可能です。例えば:
update mytable
set myscore=100
where exists (
select 1 from table1
inner join table2
on (table2.key = mytable.key)
inner join table3
on (table3.key = table2.key and table3.key = table1.key)
...
)
これは(私見では)人間を完全に混乱させます、そしてtable1
を何かにリンクすることを忘れるのはとても簡単です( "driver"テーブルには "on"節がないので)、それは合法です。
これは結合シーケンス効果だと思います。左上の結合の場合、SQLは最初に左結合を行い、次にwhereフィルタを行います。ダウナーの場合は、まずOrders.ID = 12345を見つけてから結合します。
それらのテーブルを考えてみましょう:
A
id | SomeData
B
id | id_A | SomeOtherData
id_A
はテーブルA
への外部キーです
このクエリを書く:
SELECT *
FROM A
LEFT JOIN B
ON A.id = B.id_A;
この結果を提供します。
/ : part of the result
B
+---------------------------------+
A | |
+---------------------+-------+ |
|/////////////////////|///////| |
|/////////////////////|///////| |
|/////////////////////|///////| |
|/////////////////////|///////| |
|/////////////////////+-------+-------------------------+
|/////////////////////////////|
+-----------------------------+
AにはあるがBにはないということは、BにNULL値があることを意味します。
それでは、B.id_A
の特定の部分について考えてみましょう。前の結果からそれをハイライトします。
/ : part of the result
* : part of the result with the specific B.id_A
B
+---------------------------------+
A | |
+---------------------+-------+ |
|/////////////////////|///////| |
|/////////////////////|///////| |
|/////////////////////+---+///| |
|/////////////////////|***|///| |
|/////////////////////+---+---+-------------------------+
|/////////////////////////////|
+-----------------------------+
このクエリを書く:
SELECT *
FROM A
LEFT JOIN B
ON A.id = B.id_A
AND B.id_A = SpecificPart;
この結果を提供します。
/ : part of the result
* : part of the result with the specific B.id_A
B
+---------------------------------+
A | |
+---------------------+-------+ |
|/////////////////////| | |
|/////////////////////| | |
|/////////////////////+---+ | |
|/////////////////////|***| | |
|/////////////////////+---+---+-------------------------+
|/////////////////////////////|
+-----------------------------+
これは、内部結合でB.id_A = SpecificPart
にない値を削除するためです。
それでは、クエリを次のように変更しましょう。
SELECT *
FROM A
LEFT JOIN B
ON A.id = B.id_A
WHERE B.id_A = SpecificPart;
結果は今です:
/ : part of the result
* : part of the result with the specific B.id_A
B
+---------------------------------+
A | |
+---------------------+-------+ |
| | | |
| | | |
| +---+ | |
| |***| | |
| +---+---+-------------------------+
| |
+-----------------------------+
結果全体がB.id_A = SpecificPart
に対してフィルタリングされているため、B.id_A = NULL
という部分は削除されます。これは Aにあり、Bにはありません
この区別は SQLの論理的な操作の順序 で簡単に説明できると思います。
FROM
(結合を含む)WHERE
GROUP BY
HAVING
WINDOW
SELECT
DISTINCT
UNION
、INTERSECT
、EXCEPT
ORDER BY
OFFSET
FETCH
結合はselect文の句ではなく、FROM
内の演算子です。そのため、対応するON
演算子に属するすべてのJOIN
文節は、論理処理がWHERE
文節に達するまでに "既に起こった" 論理的になっています。これは、たとえばLEFT JOIN
の場合、WHERE
句が適用されるまでに、外部結合のセマンティクスがすでに発生していることを意味します。
私はこのブログ記事で以下の例をさらに詳しく説明しました 。このクエリを実行すると:
SELECT a.actor_id, a.first_name, a.last_name, count(fa.film_id)
FROM actor a
LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
WHERE film_id < 10
GROUP BY a.actor_id, a.first_name, a.last_name
ORDER BY count(fa.film_id) ASC;
LEFT JOIN
は実際には効果がありません。俳優が映画の中で再生されなくても、そのFILM_ID
はNULL
になり、WHERE
句がそのような行をフィルタリングするため、その俳優はフィルタリングされます。結果は次のようになります。
ACTOR_ID FIRST_NAME LAST_NAME COUNT
--------------------------------------
194 MERYL ALLEN 1
198 MARY KEITEL 1
30 SANDRA PECK 1
85 MINNIE ZELLWEGER 1
123 JULIANNE DENCH 1
すなわち2つのテーブルを内部結合したかのように。フィルタ述語をON
句に移動すると、これが外部結合の基準になります。
SELECT a.actor_id, a.first_name, a.last_name, count(fa.film_id)
FROM actor a
LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
AND film_id < 10
GROUP BY a.actor_id, a.first_name, a.last_name
ORDER BY count(fa.film_id) ASC;
結果には、映画のない俳優、またはFILM_ID < 10
の映画のない俳優が含まれることになります。
ACTOR_ID FIRST_NAME LAST_NAME COUNT
-----------------------------------------
3 ED CHASE 0
4 JENNIFER DAVIS 0
5 JOHNNY LOLLOBRIGIDA 0
6 BETTE NICHOLSON 0
...
1 PENELOPE GUINESS 1
200 THORA TEMPLE 1
2 NICK WAHLBERG 1
198 MARY KEITEL 1
論理的に、述語を常に最も意味のある場所に置きます。