SQL join: предложение where против предложения on

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


в чем разница и что должно идти в каждом?

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

14 ответов


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

рассмотрим эти вопросы:

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, положения эффективно эквивалентны. Однако только потому, что они функционально одинаковы, в том, что они производят то же самое результаты, не означает, что два вида предложений имеют одинаковое смысловое значение.


  • не имеет значения для внутренних соединений
  • вопросы для внешних соединений

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

    b. ON п. - до присоединение. Записи (из правой таблицы) будут отфильтрованы перед присоединением. Это может закончиться как null в результате (так как внешний присоединяться.)



пример: рассмотрим следующие таблицы:

    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 | 

On INNER JOINs они взаимозаменяемы, и оптимизатор будет переставлять их по желанию.

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

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


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

всегда ставьте условия соединения в предложение on Если вы выполняете внутреннее соединение, поэтому не добавляйте условия where в предложение on, поместите их в предложение where

Если вы делаете левое соединение, добавьте любые условия where в предложение on для таблицы в правой части соединения. Это необходимо, потому что добавление предложения where, ссылающегося на правую сторону соединения, преобразует соединение во внутреннее соединение (за одним исключением описанный ниже.)

исключение состоит в том, что когда вы ищете записи, которые не находятся в определенной таблице, вы добавите ссылку на уникальный идентификатор(который никогда не является нулевым) в правой таблице соединения к предложению where таким образом "где t2.idfield равно null". Поэтому единственный раз, когда вы должны ссылаться на таблицу в правой части соединения, - это найти те записи, которых нет в таблице.


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

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


в этой статье ясно объясняет разницу. Он также объясняет "ON joined_condition vs, где joined_condition или joined_alias равно null".

предложение WHERE фильтрует как левую, так и правую сторону соединения, в то время как предложение ON всегда будет фильтровать только правую сторону.

  1. если вы всегда хотите получить левые боковые строки и только присоединиться, если некоторые условия совпадают, то вы должны предложение ON.
  2. если вы хотите фильтровать продукт соединения обеих сторон, тогда вы должны использовать предложение WHERE.

с точки зрения оптимизатора, не должно иметь значения, определяете ли вы свои предложения join с ON или где.

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


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

вот пример:

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.

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

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


в SQL предложение "WHERE" и " ON "являются своего рода условными Статемантами, но основное различие между ними заключается в том,что предложение "Where" используется в операторах Select/Update для указания условий, тогда как предложение " ON " используется в соединениях, где оно проверяет или проверяет, совпадают ли записи в целевой и исходной таблицах, прежде чем таблицы соединяются

например: - 'где'

выбрать * из сотрудника здесь ид_сотрудника=101

например: - 'ON'

*есть две таблицы employee и employeee_details, соответствующие столбцы employee_id.*

выбрать * из сотрудника ВНУТРЕННЕЕ СОЕДИНЕНИЕ employee_details ON сотрудник.ид_сотрудника=employee_details.ид_сотрудника

надеюсь, я ответил на ваш вопрос.Вернуться к очищения.


для повышения производительности таблицы должны иметь специальный индексированный столбец для использования для соединений .

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

таким образом, вы присоединяетесь с помощью индексированных столбцов, а затем после присоединения выполняете условие для столбца none indexed .


обычно фильтрация обрабатывается в предложении WHERE после того, как две таблицы уже были соединены. Это возможно, хотя вы можете захотеть отфильтровать одну или обе таблицы, прежде чем присоединиться к ним. Я. e, предложение where применяется ко всему результирующему набору, тогда как предложение on применяется только к рассматриваемому соединению.


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


для внутреннего соединения, 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 ни к чему (потому что таблица "драйвер" не имеет предложения "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 чтобы заставить его работать.

надеюсь, что это поможет.