Ответ
Да, 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.
Ответ 18+ 🔞
О, SQL, ёпта, классика жанра! Вечный спор, где что писать. Сейчас разжую, как есть, чтобы понятно было, а то мозг иногда от этих JOIN'ов плавится.
Смотри, в чём тут соль. ON и WHERE — это как два разных охранника на складе, блядь. Один пропускает грузовики на территорию (ON), а второй уже не пускает на конкретный этаж (WHERE). И если их обязанности перепутать, то можно остаться без нужных коробок, понимаешь?
ON — это твой сцепщик, блядь. Он говорит: «Таблицы, соединяйтесь вот по этому правилу!». Всё, что ты сюда пишешь, влияет на сам процесс склейки данных. Как будто ты говоришь: «Прицепи мне заказы к пользователям, но только если заказ выполнен, сука!». И если заказа такого нет — ну хуй с ним, пользователь-то останется.
WHERE — это уже вышибала, ёпта. Он работает после того, как всё склеили. Смотрит на готовую кучу данных и говорит: «А теперь, блядь, выкидываем отсюда всё, где заказ не выполнен!». И выкидывает напрочь, вместе с пользователем, у которого заказа вообще не было (потому что там NULL).
Для INNER JOIN — да похуй, честно. Оптимизатор умный, он часто к одному результату приведёт. Вот смотри, эти два запроса — близнецы-братья, результат одинаковый:
-- Вариант 1: Условие и связь в ON
SELECT u.name, o.product
FROM users u
INNER JOIN orders o ON u.id = o.user_id AND o.status = 'completed';
-- Вариант 2: Связь в ON, фильтр в WHERE
SELECT u.name, o.product
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.status = 'completed';
А вот где начинается настоящий пиздец и путаница — так это с LEFT JOIN. Тут уже принципиально, куда пишешь.
Пример: Нам нужны ВСЕ пользователи, даже если у них нет завершённых заказов.
-
Правильно (фильтр для заказа в
ON): «Дай всех юзеров и, если найдётся, прилепи к ним заказы, но только завершённые, блядь». Если завершённых нет — юзер всё равно будет в списке, просто поля заказа — пустые.SELECT u.name, o.product FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'completed'; -
Неправильно (фильтр в
WHERE): А вот это — ловушка для молодых. Сначала склеиваем всех юзеров со ВСЕМИ их заказами. Потом вышибала (WHERE) приходит и орёт: «Убирай нахуй всё, гдеstatusнеcompleted!». И убирает. В том числе и строки, где заказа вообще не было (там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.
А то будешь потом, как Герасим, с мешком на речку ходить, Муму топить, а потом совесть мучать будет, что не того утопил. Всё от непонимания, блядь!