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 JOIN
s они взаимозаменяемы, и оптимизатор будет переставлять их по желанию.
On OUTER JOIN
s, они не обязательно взаимозаменяемы, в зависимости от того, от какой стороны соединения они зависят.
я помещаю их в любом месте в зависимости от читаемости.
то, как я это делаю:
всегда ставьте условия соединения в предложение 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 всегда будет фильтровать только правую сторону.
- если вы всегда хотите получить левые боковые строки и только присоединиться, если некоторые условия совпадают, то вы должны предложение ON.
- если вы хотите фильтровать продукт соединения обеих сторон, тогда вы должны использовать предложение 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
чтобы заставить его работать.
надеюсь, что это поможет.