Ответ
Да, WHERE
можно и нужно использовать с JOIN
, но ON
и WHERE
выполняют разные семантические задачи, и их путаница может привести к неверным результатам, особенно с OUTER JOIN
.
ON
: Указывает условие для соединения таблиц. Фильтрация происходит до того, как строки окончательно включаются в результирующий наборJOIN
.WHERE
: Указывает условие для фильтрации уже сформированного набора данных после выполненияJOIN
.
Для INNER JOIN
разница часто незаметна:
Оптимизатор СУБД может обработать оба запроса одинаково, так как INNER JOIN
оставляет только строки, удовлетворяющие всем условиям.
-- Эти запросы вернут одинаковый результат
SELECT u.name, o.product
FROM users u
INNER JOIN orders o ON u.id = o.user_id AND o.status = 'completed';
SELECT u.name, o.product
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.status = 'completed';
Ключевое различие проявляется в OUTER JOIN
(например, LEFT JOIN
):
Представим, что мы хотим получить всех пользователей и их заказы, если они есть.
-
Условие в
ON
(Правильный способ): Фильтрует записи из правой таблицы (orders
) перед присоединением. Если у пользователя нет заказов со статусомcompleted
, он все равно останется в результате, а поля заказа будутNULL
.SELECT u.name, o.product FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'completed';
-
Условие в
WHERE
(Неправильный способ, если нужны все пользователи): СначалаLEFT JOIN
создает набор, где для пользователей без заказов поляorders
равныNULL
. ЗатемWHERE o.status = 'completed'
отфильтровывает все строки, где это условие ложно, включая те, гдеo.status
равенNULL
. В итогеLEFT JOIN
эффективно превращается вINNER JOIN
.SELECT u.name, o.product FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.status = 'completed'; -- Отфильтрует пользователей без заказов
Итог:
- Условия, определяющие связь между таблицами, помещайте в
ON
. - Условия, фильтрующие итоговый результат, помещайте в
WHERE
.