Найти клиентов, ни разу не сделавших заказ
Таблицы Customers(id, name) и Orders(id, customer_id). Верните клиентов, у которых нет ни одного заказа.
Требования:
по nullable-столбцу может молча вернуть пусто)
- это анти-join — клиенты без совпавших заказов
- результат должен быть устойчив к
NULLна стороне заказов (наивныйNOT IN
SELECT c.id, c.name
FROM Customers c
-- ваш запрос здесь
Напишите запрос.
LEFT JOIN Orders к Customers по id клиента, затем фильтр WHERE o.customer_id IS NULL — анти-join оставляет клиентов без совпавшего заказа. NOT EXISTS эквивалентен; NOT IN вернёт пусто, если в подзапросе есть NULL, поэтому предпочтительнее LEFT JOIN ... IS NULL или NOT EXISTS.
- ✗Использовать
INNER JOINсIS NULL, который никогда не совпадёт - ✗Доверять
NOT IN, когда подзапрос может содержатьNULL - ✗Забывать, что проверка
IS NULLдолжна быть по присоединённому (правому) столбцу
- →Почему
NOT INне возвращает строк, когда подзапрос даётNULL? - →Как
LEFT JOIN ... IS NULLиNOT EXISTSсравниваются по производительности?
Задача
Найти клиентов, которые ни разу не оформили заказ.
SELECT c.id, c.name
FROM Customers c
LEFT JOIN Orders o ON c.id = o.customer_id
WHERE o.customer_id IS NULL;
Как это работает
Паттерн анти-join: «строки в A, у которых нет соответствия в B».
LEFT JOIN оставляет всех клиентов; у тех, у кого есть заказы, столбцы из Orders заполнены, а у тех, у кого заказов нет, столбцы правой таблицы — NULL. Условие WHERE o.customer_id IS NULL оставляет именно вторых.
Альтернативы:
WHERE NOT EXISTS (SELECT 1 FROM Orders o WHERE o.customer_id = c.id)— эквивалентно и часто эффективно.WHERE id NOT IN (SELECT customer_id FROM Orders)— работает, но если подзапрос вернёт хоть одинNULL, весьNOT INдаст пустой результат (трёхзначная логика).
⚠️ Используйте LEFT JOIN ... IS NULL или NOT EXISTS; остерегайтесь NOT IN с NULL.