Собесов

alexeygrigorev/data-science-interviews: клиенты без заказов

SQLJOIN-ыЛёгкаяJunior

Условие

В customers(id, name) хранятся все клиенты, в orders(id, customer_id, ts) — их заказы. Выведите имена клиентов, у которых нет ни одного заказа.

Решение

Подход

Три эквивалентных способа: LEFT JOIN ... WHERE orders.id IS NULL, NOT EXISTS, NOT IN. В реальной жизни оптимизаторы Postgres и MySQL хорошо переписывают первые два в анти-джойн. NOT IN опасен из-за NULL.

Реализация

-- LEFT JOIN + IS NULL
SELECT c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;
 
-- NOT EXISTS (часто самый читаемый)
SELECT c.name
FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.id
);
 
-- NOT IN — осторожно с NULL
SELECT c.name
FROM customers c
WHERE c.id NOT IN (
    SELECT customer_id FROM orders WHERE customer_id IS NOT NULL
);

Подводные камни

  1. NOT IN + NULL. Если подзапрос вернёт хотя бы один NULL, весь NOT IN даст пустой результат — из-за трёхзначной логики. Всегда фильтруйте IS NOT NULL внутри подзапроса или используйте NOT EXISTS.
  2. LEFT JOIN с фильтром по правой таблице в WHERE — типичная ошибка: фильтрация в WHERE превращает LEFT JOIN в INNER JOIN. Условие проверки на IS NULL корректно только для колонок правой таблицы.
  3. Дубликаты клиентов в результате: если у клиента «нет заказов» и таблица customers уникальна по id, LEFT JOIN даст одну строку. Но если в customers дубликаты — поможет DISTINCT.
  4. На больших таблицах NOT EXISTS часто работает быстрее LEFT JOIN + IS NULL благодаря early-exit.

Эталонный ответ

LEFT JOIN orders ... WHERE o.id IS NULL или NOT EXISTS (SELECT 1 FROM orders WHERE customer_id = c.id). NOT IN без IS NOT NULL ломается на NULL-ах.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти