Разберите следующий SQL-кейс: "SELECT users.id, orders.id FROM users LEFT JOIN orders ON users.id = orders.user_id WHERE orders.status = 'paid';" — что непреднамеренно делает этот запрос и как его править для корректного результата
Что непреднамеренно делает запрос SELECT users.id, orders.id FROM users LEFT JOIN orders ON users.id = orders.user_id WHERE orders.status = 'paid'; - Из‑за фильтра в WHERE строки, у которых нет совпадающего заказа, получают значения полей orders = NULL, и условие orders.status=′paid′orders.status = 'paid'orders.status=′paid′ для них ложно (NULL ≠ 'paid'). Поэтому такие пользователи вычёркиваются — по сути LEFT JOIN превращается в INNER JOIN по этому условию. Почему так происходит (коротко) - JOIN выполняется первым, а WHERE фильтрует результат джойна. Условие на поля правой таблицы в WHERE удаляет строки с NULL. Эквивалентная логика (в KaTeX) \( \text{LEFT JOIN ... WHERE orders.status = 'paid'} \;\Longrightarrow\; \text{INNER JOIN ON users.id = orders.user_id AND orders.status = 'paid'} \) Как править в зависимости от цели 1) Если вы хотите получить всех пользователей и только их оплаченные заказы (в том числе пользователей без оплаченных заказов — с NULL в orders.id): - Перенести условие в ON: SELECT users.id, orders.id FROM users LEFT JOIN orders ON users.id = orders.user_id AND orders.status = 'paid'; 2) Если вы хотите только пользователей, у которых есть оплаченные заказы (т.е. фактически INNER JOIN): - Использовать INNER JOIN или оставить исходный WHERE: SELECT users.id, orders.id FROM users INNER JOIN orders ON users.id = orders.user_id WHERE orders.status = 'paid'; 3) Альтернативный паттерн (реже нужен): вернуть пользователей с оплач. заказами и тех, у кого нет заказов вообще (но исключить тех, у кого есть только неоплаченные заказы): SELECT users.id, orders.id FROM users LEFT JOIN orders ON users.id = orders.user_id WHERE orders.status = 'paid' OR orders.id IS NULL; Короткое резюме - Перенесите фильтр по правой таблице в ON, если хотите сохранить семантику LEFT JOIN и показывать пользователей без подходящих заказов; оставьте WHERE (или используйте INNER JOIN), если хотите только строки с существующими оплач. заказами.
SELECT users.id, orders.id
FROM users
LEFT JOIN orders ON users.id = orders.user_id
WHERE orders.status = 'paid';
- Из‑за фильтра в WHERE строки, у которых нет совпадающего заказа, получают значения полей orders = NULL, и условие orders.status=′paid′orders.status = 'paid'orders.status=′paid′ для них ложно (NULL ≠ 'paid'). Поэтому такие пользователи вычёркиваются — по сути LEFT JOIN превращается в INNER JOIN по этому условию.
Почему так происходит (коротко)
- JOIN выполняется первым, а WHERE фильтрует результат джойна. Условие на поля правой таблицы в WHERE удаляет строки с NULL.
Эквивалентная логика (в KaTeX)
\( \text{LEFT JOIN ... WHERE orders.status = 'paid'} \;\Longrightarrow\;
\text{INNER JOIN ON users.id = orders.user_id AND orders.status = 'paid'} \)
Как править в зависимости от цели
1) Если вы хотите получить всех пользователей и только их оплаченные заказы (в том числе пользователей без оплаченных заказов — с NULL в orders.id):
- Перенести условие в ON:
SELECT users.id, orders.id
FROM users
LEFT JOIN orders ON users.id = orders.user_id AND orders.status = 'paid';
2) Если вы хотите только пользователей, у которых есть оплаченные заказы (т.е. фактически INNER JOIN):
- Использовать INNER JOIN или оставить исходный WHERE:
SELECT users.id, orders.id
FROM users
INNER JOIN orders ON users.id = orders.user_id
WHERE orders.status = 'paid';
3) Альтернативный паттерн (реже нужен): вернуть пользователей с оплач. заказами и тех, у кого нет заказов вообще (но исключить тех, у кого есть только неоплаченные заказы):
SELECT users.id, orders.id
FROM users
LEFT JOIN orders ON users.id = orders.user_id
WHERE orders.status = 'paid' OR orders.id IS NULL;
Короткое резюме
- Перенесите фильтр по правой таблице в ON, если хотите сохранить семантику LEFT JOIN и показывать пользователей без подходящих заказов; оставьте WHERE (или используйте INNER JOIN), если хотите только строки с существующими оплач. заказами.