Найти пользователей, что зарегистрировались, но не покупали — и почему NOT IN даёт ноль строк
Таблицы users(user_id) и orders(user_id), где orders.user_id равен NULL в нескольких строках гостевых заказов. Верните каждого пользователя, что зарегистрировался, но не сделал ни одного заказа. Учтите — версия с NOT IN (SELECT user_id FROM orders) даёт на этих данных ноль строк.
-- верните users.user_id для пользователей без совпадающей строки заказа
Напишите запрос.
Анти-join: SELECT u.user_id FROM users u LEFT JOIN orders o ON o.user_id = u.user_id WHERE o.user_id IS NULL, либо NOT EXISTS. NOT IN (SELECT user_id FROM orders) даёт ноль строк при NULL в подзапросе, ведь x NOT IN (…, NULL) равно UNKNOWN.
- ✗Доверять NOT IN, когда в подзапросе может быть NULL
- ✗Брать INNER JOIN, который полностью отбрасывает несовпавших
- ✗Фильтровать o.user_id IS NULL после inner join
- →Как NOT EXISTS обходит проблему с NULL?
- →Починит ли NOT IN отсев NULL из подзапроса?
Return the users with no matching order via an anti-join. The LEFT JOIN keeps every user and pads the right side with NULL when there is no order; filtering o.user_id IS NULL keeps exactly the non-purchasers.
SELECT u.user_id
FROM users u
LEFT JOIN orders o ON o.user_id = u.user_id
WHERE o.user_id IS NULL;
NOT EXISTS is equally correct and NULL-safe:
SELECT u.user_id
FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.user_id);
NOT IN (SELECT user_id FROM orders) breaks because a single NULL in the list makes user_id NOT IN (..., NULL) evaluate to UNKNOWN for every row — never TRUE — so the whole result is empty.