それを読んだ後、これはExplicit vs Implicit SQL Joinsの複製ではありません。答えは関連しているかもしれません(または同じかもしれません)が、質問は異なります。
違いは何ですか?それぞれに何を入れる必要がありますか?
理論を正しく理解していれば、クエリ オプティマイザーは両方を同じ意味で使用できるはずです。
それを読んだ後、これはExplicit vs Implicit SQL Joinsの複製ではありません。答えは関連しているかもしれません(または同じかもしれません)が、質問は異なります。
違いは何ですか?それぞれに何を入れる必要がありますか?
理論を正しく理解していれば、クエリ オプティマイザーは両方を同じ意味で使用できるはずです。
それらは同じものではありません。
次のクエリを検討してください。
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
1 つ目は、注文番号 の注文とその行 (存在する場合) を返し12345
ます。12345
2 番目はすべての注文を返しますが、関連付けられた行を持つのは注文だけです。
を使用するINNER JOIN
と、句は事実上同等になります。ただし、同じ結果を生成するという点で機能的に同じだからといって、2 種類の節が同じ意味を持つわけではありません。
INNER JOIN
s ではそれらは交換可能であり、オプティマイザはそれらを自由に再配置します。
sOUTER JOIN
では、それらが依存する結合の側に応じて、必ずしも交換可能であるとは限りません。
読みやすさに応じて、どちらかの場所に配置します。
私がそれを行う方法は次のとおりです。
ON
を実行している場合は、必ず節に結合条件を入れてくださいINNER JOIN
。したがって、WHERE 条件を ON 句に追加しないでください。句に入れますWHERE
。
を実行している場合は、結合の右側にあるテーブルLEFT JOIN
の句に WHERE 条件を追加します。結合の右側を参照する WHERE 句を追加すると、結合が INNER JOIN に変換されるため、これは必須です。ON
例外は、特定のテーブルにないレコードを探している場合です。次のように、RIGHT JOIN テーブルの一意の識別子 (決して NULL ではない) への参照を WHERE 句に追加しますWHERE t2.idfield IS NULL
。したがって、結合の右側にあるテーブルを参照する必要があるのは、テーブルにないレコードを見つけるときだけです。
内部結合では、それらは同じことを意味します。ただし、結合条件を WHERE 句と ON 句のどちらに置くかによって、外部結合で異なる結果が得られます。this related questionとthis answer (by me) を見てください。
クエリを読んでいる人にとってより明確になるため、結合条件を常にON句に入れる習慣を身に付けることが最も理にかなっていると思います(それが外部結合であり、実際にwhere句に入れたい場合を除く)。テーブルが結合されている条件と、WHERE 句が何十行も長くなるのを防ぐのにも役立ちます。
次post
のpost_comment
表があるとします。
にはpost
次の記録があります。
| id | title |
|----|-----------|
| 1 | Java |
| 2 | Hibernate |
| 3 | JPA |
にpost_comment
は次の 3 つの行があります。
| id | review | post_id |
|----|-----------|---------|
| 1 | Good | 1 |
| 2 | Excellent | 1 |
| 3 | Awesome | 2 |
SQL JOIN 句を使用すると、異なるテーブルに属する行を関連付けることができます。たとえば、CROSS JOINは、結合する 2 つのテーブル間のすべての可能な行の組み合わせを含むデカルト積を作成します。
CROSS JOIN は特定のシナリオで役立ちますが、ほとんどの場合、特定の条件に基づいてテーブルを結合する必要があります。そこで、INNER JOIN の出番です。
SQL INNER JOIN を使用すると、ON 句で指定された条件に基づいて、2 つのテーブルを結合するデカルト積をフィルター処理できます。
「常に真」の条件を指定すると、INNER JOIN は結合されたレコードをフィルタリングせず、結果セットには 2 つの結合テーブルのデカルト積が含まれます。
たとえば、次の SQL INNER JOIN クエリを実行するとします。
SELECT
p.id AS "p.id",
pc.id AS "pc.id"
FROM post p
INNER JOIN post_comment pc ON 1 = 1
post
とpost_comment
レコードのすべての組み合わせを取得します。
| p.id | pc.id |
|---------|------------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
したがって、ON 句の条件が「常に真」の場合、INNER JOIN は単純に CROSS JOIN クエリと同等です。
SELECT
p.id AS "p.id",
pc.id AS "pc.id"
FROM post p
CROSS JOIN post_comment
WHERE 1 = 1
ORDER BY p.id, pc.id
一方、ON 句の条件が「常に false」の場合、結合されたすべてのレコードが除外され、結果セットは空になります。
したがって、次の SQL INNER JOIN クエリを実行すると、次のようになります。
SELECT
p.id AS "p.id",
pc.id AS "pc.id"
FROM post p
INNER JOIN post_comment pc ON 1 = 0
ORDER BY p.id, pc.id
結果は返されません。
| p.id | pc.id |
|---------|------------|
これは、上記のクエリが次の CROSS JOIN クエリと同等であるためです。
SELECT
p.id AS "p.id",
pc.id AS "pc.id"
FROM post p
CROSS JOIN post_comment
WHERE 1 = 0
ORDER BY p.id, pc.id
最も一般的な ON 句の条件は、次のクエリに示すように、子テーブルの外部キー列を親テーブルの主キー列と一致させるものです。
SELECT
p.id AS "p.id",
pc.post_id AS "pc.post_id",
pc.id AS "pc.id",
p.title AS "p.title",
pc.review AS "pc.review"
FROM post p
INNER JOIN post_comment pc ON pc.post_id = p.id
ORDER BY p.id, pc.id
上記の SQL INNER JOIN クエリを実行すると、次の結果セットが得られます。
| p.id | pc.post_id | pc.id | p.title | pc.review |
|---------|------------|------------|------------|-----------|
| 1 | 1 | 1 | Java | Good |
| 1 | 1 | 2 | Java | Excellent |
| 2 | 2 | 3 | Hibernate | Awesome |
そのため、ON 句の条件に一致するレコードのみがクエリ結果セットに含まれます。この場合、結果セットにはすべてのレコードが含まれpost
ています。関連付けられていない行は、ON 句post_comment
の条件を満たすことができないため、除外されます。post
post_comment
繰り返しますが、上記の SQL INNER JOIN クエリは、次の CROSS JOIN クエリと同等です。
SELECT
p.id AS "p.id",
pc.post_id AS "pc.post_id",
pc.id AS "pc.id",
p.title AS "p.title",
pc.review AS "pc.review"
FROM post p, post_comment pc
WHERE pc.post_id = p.id
非ストライク行は、WHERE 句を満たす行であり、これらのレコードのみが結果セットに含まれます。これは、INNER JOIN 句がどのように機能するかを視覚化する最良の方法です。
| | p.id | pc.post_id | pc.id | p.タイトル | PCレビュー | |------|------------|-------|-----------|--------- --| | | 1 | 1 | 1 | ジャワ | 良い | | | 1 | 1 | 2 | ジャワ | 素晴らしい || | 1 | 2 | 3 | ジャワ | すごい || | 2 | 1 | 1 | 休止状態 | 良い || | 2 | 1 | 2 | 休止状態 | 素晴らしい || | 2 | 2 | 3 | 休止状態 | すごい || | 3 | 1 | 1 | JPA | JPA | 良い || | 3 | 1 | 2 | JPA | JPA | 素晴らしい || | 3 | 2 | 3 | JPA | JPA | すごい |
INNER JOIN ステートメントは、INNER JOIN クエリの ON 句で使用したのと同じ条件に一致する WHERE 句を使用して、CROSS JOIN として書き直すことができます。
これは、OUTER JOIN ではなく、INNER 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 | |
+-------+-------------+------+-----+---------+-------+
fid はテーブル 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.v = 'K' に対して、最初のクエリが t1 からのレコードと、t2 からの従属行 (ある場合) を返すことは明らかです。
2 番目のクエリは t1 から行を返しますが、t1.v = 'K' の場合のみ関連する行があります。
オプティマイザーに関しては、結合句を ON で定義するか WHERE で定義するかに違いはありません。
ただし、私見では、結合を実行するときに ON 句を使用する方がはるかに明確だと思います。そうすれば、結合を処理する方法と、残りの WHERE 句と混合する方法を指示する、クエリの特定のセクションを作成できます。
結合シーケンス効果だと思います。左上結合の場合、SQL は最初に左結合を実行し、次に where フィルターを実行します。最悪のケースでは、最初に Orders.ID=12345 を見つけてから参加します。
パフォーマンスを向上させるには、テーブルに JOINS に使用する特別なインデックス付きの列が必要です。
したがって、条件付けする列がこれらのインデックス付き列のいずれでもない場合は、それを WHERE に保持する方がよいと思います。
したがって、インデックス付きの列を使用して結合し、結合した後、インデックスなしの列で条件を実行します。
それらは文字通り同等です。
ほとんどのオープンソース データベース ( MySqlとpostgresqlの最も顕著な例) では、クエリ プランニングは、リレーショナル データベース管理システムにおけるアクセス パスの選択 (Selinger et al, 1979) に登場する古典的なアルゴリズムの変形です。このアプローチでは、条件は 2 種類あります
特にMySqlでは、オプティマイザーをトレースすることで、解析中に条件が同等の条件に置き換えられることを確認できます。同様のことが postgresql でも発生します (ログから確認する方法はありませんが、ソースの説明を読む必要があります)。join .. on
where
とにかく、主なポイントは、2 つの構文バリアントの違いは、解析/クエリ書き換えフェーズで失われ、クエリの計画と実行のフェーズにさえ到達しないということです。したがって、パフォーマンスの点で同等であるかどうかは疑問の余地がなく、実行フェーズに到達するずっと前に同一になります。
を使用explain
して、それらが同一の計画を作成することを確認できます。たとえば、postgres では、構文をどこにも使用していなくても、プランに句が含まれjoin
join..on
ます。
Oracle と SQL サーバーはオープン ソースではありませんが、私の知る限り、それらは等価規則 (リレーショナル代数の規則と同様) に基づいており、どちらの場合も同一の実行計画を生成します。
明らかに、2 つの構文スタイルは、構文を使用する必要がある外部結合では同等ではありません。
join ... on
これが私の解決策です。
SELECT song_ID,songs.fullname, singers.fullname
FROM music JOIN songs ON songs.ID = music.song_ID
JOIN singers ON singers.ID = music.singer_ID
GROUP BY songs.fullname
あなたはそれを機能させるために持っている必要がありますGROUP BY
。
この助けを願っています。