web-dev-qa-db-ja.com

SQL結合:where句とon句

それを読んだ後、これはnotExplicit vs Implicit SQL Joins の複製です。答えは関係しているかもしれませんが(あるいは同じでさえも)、 question は異なります。


違いは何ですか?それぞれに何を入れるべきですか?

理論を正しく理解すれば、クエリオプティマイザは両方を同じ意味で使用できるはずです。

574
BCS

それらは同じものではありません。

これらの質問を考えなさい:

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種類の句が同じ意味を持つわけではありません。

751
Joel Coehoorn
  • 内部結合には関係ありません
  • 外部結合に関する事項

    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 | 
249
Sandeep Jindal

INNER JOINs上でそれらは交換可能であり、オプティマイザはそれらを自由に再配置します。

OUTER JOINsでは、結合のどちら側に依存しているかによって、それらは必ずしも交換可能ではありません。

読みやすさに応じてどちらかに配置します。

140
Cade Roux

やり方は次のとおりです。

  • INNER JOINを実行している場合は、結合条件を常にON節に入れてください。そのため、ON句にWHERE条件を追加しないで、それらをWHERE句に入れてください。

  • LEFT JOINを実行している場合は、結合の right 側にあるテーブルのON句にWHERE条件を追加します。結合の右側を参照するWHERE句を追加すると、結合がINNER JOINに変換されるため、これは必須です。

    例外は、特定のテーブルにないレコードを探しているときです。このようにして、RIGHT JOINテーブルの一意の識別子(これはNULLではない)への参照をWHERE句に追加します。WHERE t2.idfield IS NULL。そのため、結合の右側にあるテーブルを参照する必要があるのは、そのテーブルにないレコードを見つけるときだけです。

40
HLGEM

内部結合でも、それらは同じことを意味します。ただし、WHERE vs ON句で結合条件を指定したかどうかによって、外部結合では異なる結果が得られます。 この関連した質問 そして この答え /を見てください。

クエリを読む人にとってより明確になるように、結合条件を常にON句に入れる習慣があるのが最も理にかなっていると思います(外部結合であり、実際にはwhere句にしたくない場合を除く)。テーブルが結合されている条件、およびWHERE句が数十行になるのを防ぐのに役立ちます。

30
matt b

この記事 は明らかに違いを説明しています。また、「ON join_conditionとWHERE join_conditionまたはjoint_aliasがnull」との説明もあります。

WHERE句はFROM句の結果をJOINとともにフィルタリングし、ON句はFROMテーブルとJOINテーブルの間のテーブル結果を生成するために使用されます。

  1. 2つのテーブルを結合するテーブル結果を生成したい場合は、ON句を使用してテーブルの結合方法を決定する必要があります。もちろん、これは例えばINNER JOINの場合、元のテーブルから行をフィルタリングすることもできます。
  2. 両側を結合した積をフィルタリングしたい場合は、WHERE句を使用してください。
21
Vlad Mihalcea

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'の場合のみ、関連付けられた行が含まれます。

11

オプティマイザの観点からは、結合句をONで定義してもWHEREで定義しても違いはありません。

しかし、私見、結合を実行するときにON句を使用する方がはるかに明確だと思います。このようにして、結合がどのように処理されるのか、それ以外のWHERE句と混在するのかを指定する、クエリの特定のセクションがあります。

8
Grant Limberg

パフォーマンスを向上させるために、テーブルはJOINSに使用する特別なインデックスカラムを持つべきです。

だからあなたが条件付けたカラムがそれらのインデックスカラムの一つでないならば、私はそれをWHEREにそれを保存するのが良いと思います。

そのため、インデックス付き列を使用して結合し、結合後にインデックスなしの列に対して条件を実行します。

1
yakoub abaya

データを結合しようとしていますか、それともデータをフィルタしようとしていますか?

読みやすくするために、これらのユースケースをそれぞれONとWHEREに分離することが最も理にかなっています。

  • oNでデータを結合
  • wHEREでデータをフィルタリング

WHERE句にJOIN条件とフィルタ条件が存在するクエリを読むのは非常に困難になります。

パフォーマンスの面では違いはありませんが、SQLの種類によってクエリプランニングの扱いが異なる場合があります。したがって、¯\_(ツ)_/¯を試してみる価値があります(クエリ速度に影響するキャッシュに注意してください)。

また、他の人が指摘したように、外部結合を使用した場合、ON句にフィルタ条件を指定した場合、結果が異なるのは1つのテーブルにのみ影響するためです。

私はここでこれについてより詳細な投稿を書きました: https://dataschool.com/learn/difference-between-where-and-on-in-sql

1
matthew david

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

あなたの質問に答えてください。説明を元に戻します。

1
Sharon Fernando

通常、フィルタリングは2つのテーブルが結合された後にWHERE句で処理されます。結合する前に、一方または両方のテーブルをフィルタ処理することをお勧めします。つまり、where句は結果セット全体に適用されますが、on句は問題の結合にのみ適用されます。

1
sree

内部結合の場合は、WHEREONを同じ意味で使用できます。実際、相関副照会で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"節がないので)、それは合法です。

1
Austin Barry

これは結合シーケンス効果だと思います。左上の結合の場合、SQLは最初に左結合を行い、次にwhereフィルタを行います。ダウナーの場合は、まずOrders.ID = 12345を見つけてから結合します。

1
Xing-Wei Lin

それらのテーブルを考えてみましょう:

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にはありません

0
Cid

この区別は SQLの論理的な操作の順序 で簡単に説明できると思います。

  • FROM(結合を含む)
  • WHERE
  • GROUP BY
  • 集計
  • HAVING
  • WINDOW
  • SELECT
  • DISTINCT
  • UNIONINTERSECTEXCEPT
  • 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_IDNULLになり、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

要するに

論理的に、述語を常に最も意味のある場所に置きます。

0
Lukas Eder