SQL-соединение: где предложение по сравнению с предложением - PullRequest
585 голосов
/ 09 декабря 2008

После прочтения это , а не дубликат Явное против неявного SQL-соединения Ответ может быть связан (или даже один и тот же), но вопрос отличается.


Какая разница и что должно быть в каждом?

Если я правильно понимаю теорию, оптимизатор запросов должен иметь возможность использовать их как взаимозаменяемые.

Ответы [ 17 ]

760 голосов
/ 09 декабря 2008

Они не одно и то же.

Рассмотрим эти запросы:

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. Второй вернет все ордера, но только с ордером 12345 будут связаны любые строки.

При INNER JOIN пункты эффективно эквивалентны. Однако то, что они функционально одинаковы и дают одинаковые результаты, не означает, что два вида предложений имеют одинаковое семантическое значение.

260 голосов
/ 08 января 2014
  • Не имеет значения для внутренних соединений
  • Вопросы для внешних соединений

    а. WHERE предложение: После присоединения . Записи будут отфильтрованы после объединения.

    б. ON предложение - До присоединения . Записи (из правой таблицы) будут отфильтрованы перед присоединением. Это может закончиться как нуль в результате (так как 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     |

а) Внутри 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  | 

б) Внутри 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 | 
140 голосов
/ 09 декабря 2008

На INNER JOIN s они взаимозаменяемы, и оптимизатор переставит их по желанию.

В OUTER JOIN s они не обязательно взаимозаменяемы, в зависимости от того, от какой стороны соединения они зависят.

Я помещаю их в любом месте в зависимости от читабельности.

41 голосов
/ 09 декабря 2008

Как я это делаю:

  • Всегда ставьте условия соединения в предложении ON, если вы делаете INNER JOIN. Поэтому не добавляйте условия WHERE в предложение ON, поместите их в предложение WHERE.

  • Если вы выполняете LEFT JOIN, добавьте любые условия WHERE к предложению ON для таблицы со стороны справа объединения. Это необходимо, поскольку добавление предложения WHERE, которое ссылается на правую сторону объединения, преобразует соединение в INNER JOIN.

    Исключение составляют случаи, когда вы ищете записи, которых нет в определенной таблице. Вы бы добавили ссылку на уникальный идентификатор (который никогда не равен NULL) в таблице RIGHT JOIN к предложению WHERE следующим образом: WHERE t2.idfield IS NULL. Таким образом, единственный раз, когда вы должны ссылаться на таблицу с правой стороны объединения, это найти те записи, которых нет в таблице.

30 голосов
/ 09 декабря 2008

На внутреннем соединении они означают одно и то же. Однако вы получите разные результаты во внешнем соединении в зависимости от того, поместите ли вы условие соединения в предложение WHERE vs ON. Взгляните на этот связанный вопрос и этот ответ (мной).

Я думаю, что имеет смысл использовать привычку всегда включать условие соединения в предложение ON (если только это не внешнее соединение, а вы действительно хотите его в предложении where), так как оно делает его понятнее для всех чтение вашего запроса при каких условиях соединяются таблицы, а также это помогает предотвратить длину предложения WHERE в десятки строк.

22 голосов
/ 25 мая 2014

Эта статья четко объясняет разницу. Это также объясняет, что "ON join_condition vs WHERE join_condition или join_alias имеет значение null".

Предложение WHERE фильтрует результат предложения FROM вместе с JOIN, а предложение ON используется для получения результата таблицы между таблицами FROM и JOIN.

  1. Если вы хотите получить результат таблицы, который объединяет две таблицы, то вам следует использовать предложение ON, чтобы определить, как таблицы объединяются. Конечно, это также может фильтровать строки из исходной таблицы, например, в случае INNER JOIN.
  2. Если вы хотите отфильтровать продукт объединения обеих сторон, вам следует использовать предложение WHERE.
11 голосов
/ 13 марта 2016

Существует большая разница между , где предложение против в предложении , когда речь идет о левом соединении.

Вот пример:

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.

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 clause":

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'.

Второй запрос возвращает строки из t1, но только для t1.v = 'K' будет иметь любую связанную строку с ним.

8 голосов
/ 09 декабря 2008

С точки зрения оптимизатора, не должно иметь значения, определяете ли вы свои предложения соединения с помощью ON или WHERE.

Однако, ИМХО, я думаю, гораздо проще использовать предложение ON при выполнении объединений. Таким образом, у вас есть определенный раздел вашего запроса, который определяет, как обрабатывается объединение, а не смешивается с остальными предложениями WHERE.

1 голос
/ 17 мая 2014

Для внутреннего соединения 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"), но это законно.

1 голос
/ 07 января 2014

Я думаю, что это эффект последовательности соединения. В верхнем левом случае объединения сначала выполняется левое соединение SQL, а затем - фильтр там. В случае сбоя сначала найдите Orders.ID = 12345, а затем присоединитесь.

...