0Pricing
SQL Interview Prep · Урок

Поиск строк без совпадений (антиобъединение)

Шаблон LEFT JOIN и IS NULL для поиска потерянных и отсутствующих данных.

«Поиск строк без совпадений (антиобъединение)» — бесплатный урок SQL Interview Prep на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Interview Prep содержит 4 уроков всего.

Вопрос об антисоединении

Один из самых частых вопросов о внешних соединениях: «Найдите клиентов, которые никогда не размещали заказ». Или: «Перечислите товары, которые никогда не продавались» или «заказы без соответствующего клиента».

У всех этих задач одна структура: строки одной таблицы, для которых нет соответствия в другой. Чистый идиоматический подход — антисоединение, построенное с помощью LEFT JOIN и фильтра IS NULL.

Основная идея

Начните с LEFT JOIN: он сохраняет каждую левую строку, а в столбцах правой таблицы для несовпавших левых строк появляются NULL.

Значит, несовпавшие строки — это ровно те строки, где столбец правой таблицы равен NULL. Отфильтруйте их, и вы выделите строки без соответствия. В этом и заключается весь приём.

Построение шаблона

Ниже приведён канонический пример антисоединения для поиска клиентов без заказов. Читайте его в два этапа: LEFT JOIN сохраняет всех клиентов, затем WHERE o.customer_id IS NULL оставляет только несовпавших.

SELECT c.id, c.name
FROM customers c
LEFT JOIN orders o
  ON o.customer_id = c.id
WHERE o.customer_id IS NULL;
-- only customers with zero orders

Почему это работает: шаг за шагом

Проследим выполнение на наших данных, где у Carol нет заказов:

  • LEFT JOIN создаёт Alice (x2), Bob (x1) и Carol с NULL в столбцах правой таблицы.
  • WHERE o.customer_id IS NULL отбрасывает Alice и Bob, поскольку в их столбцах правой таблицы находятся настоящие значения.
  • Сохраняется только строка Carol — строка с добавленными NULL.

Фильтр выполняется после соединения, поэтому он видит эти NULL и выбирает именно строки-сироты.

Выбор правильного столбца для проверки

Проверяйте столбец правой таблицы, который в корректном совпадении никогда не может законно иметь значение NULL, — в идеале ключ соединения или первичный ключ.

Если проверить допускающий NULL столбец, например o.shipped_at, вы также выберете существующие, но ещё не отправленные заказы, что будет неверно. Проверка o.customer_id (ключа соединения) или o.id (его первичного ключа) гарантирует, что NULL означает «ни одна строка не совпала».

-- SAFE: join key / primary key
WHERE o.id IS NULL

-- RISKY: a nullable data column
WHERE o.shipped_at IS NULL  -- catches unshipped too!

Антисоединение и NOT IN

Интервьюеры сравнивают антисоединение с NOT IN. Они выглядят эквивалентными, но по-разному обрабатывают NULL.

Если подзапрос возвращает хотя бы один NULL, NOT IN не возвращает вообще ни одной строки — это печально известная скрытая ошибка. Антисоединение с LEFT JOIN / IS NULL от неё не зависит.

-- DANGEROUS if any customer_id is NULL
SELECT id, name FROM customers
WHERE id NOT IN (SELECT customer_id FROM orders);

-- SAFE anti-join, same intent
SELECT c.id, c.name FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;

Антисоединение и NOT EXISTS

Другой эквивалентный вариант — NOT EXISTS с коррелированным подзапросом. Он также корректно обрабатывает NULL и часто работает так же быстро.

Все три варианта (LEFT JOIN/IS NULL, NOT EXISTS, NOT IN) позволяют выразить антисоединение, но на собеседовании предпочитайте LEFT JOIN/IS NULL или NOT EXISTS за корректную обработку NULL. Упоминание ловушки NOT IN принесёт дополнительные баллы.

SELECT c.id, c.name
FROM customers c
WHERE NOT EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.id
);

Распространённая ошибка

Частая ошибка — поместить условие отсутствия совпадения в предложение ON, а не в WHERE.

Запись ... ON o.customer_id = c.id AND o.id IS NULL не фильтрует результат: она лишь меняет критерий совпадения, и каждый клиент по-прежнему сохраняется в LEFT JOIN. Проверка IS NULL должна находиться в WHERE и применяться после соединения. Подробно этот случай разбирается в следующем уроке.

Поиск осиротевших дочерних строк

Этот шаблон работает и в обратном направлении. Чтобы найти заказы, ссылающиеся на отсутствующего клиента (осиротевшие строки, проверка целостности данных), сохраните orders и проверьте сторону клиента на NULL.

SELECT o.id AS order_id, o.customer_id
FROM orders o
LEFT JOIN customers c
  ON c.id = o.customer_id
WHERE c.id IS NULL;
-- orders pointing to a non-existent customer

Подсчёт осиротевших строк

Часто требуется всего лишь количество: «Сколько клиентов никогда не делали заказ?» Оберните антисоединение или подсчитайте напрямую.

Поскольку антисоединение уже возвращает по одной строке на каждую осиротевшую строку, обычный COUNT(*) здесь корректен: на каждого несовпавшего клиента приходится ровно одна строка.

SELECT COUNT(*) AS never_ordered
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;

Универсальный шаблон

Запомните этот каркас из трёх строк: он решает огромное количество задач на собеседованиях:

  • FROM keep_table k
  • LEFT JOIN other o ON o.fk = k.id
  • WHERE o.id IS NULL

Меняйте таблицы и ключи, чтобы находить непроданные товары, неназначенные обращения, пользователей без входов в систему — всё, что описывается как «X без соответствующего Y».

Быстрая проверка

Вам нужны товары, которые никогда не встречались в элементах заказов.

Итоги

Антисоединение находит строки без соответствия: LEFT JOIN, затем WHERE right_key IS NULL.

  • Проверяйте ключ соединения или первичный ключ, но никогда не допускающий NULL столбец с данными.
  • Проверка IS NULL должна находиться в WHERE, а не в ON.
  • Этот подход эквивалентен NOT EXISTS; предпочитайте его NOT IN, который даёт сбой при наличии NULL.
  • Поменяйте таблицы местами, чтобы найти осиротевшие дочерние строки.

Один шаблон — множество задач: «X без соответствующего Y».

Часто задаваемые вопросы

Урок «Поиск строк без совпадений (антиобъединение)» бесплатный?

Да — полный текст урока «Поиск строк без совпадений (антиобъединение)» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Interview Prep, подпишись на CoddyKit PRO. Курс SQL Interview Prep содержит 4 уроков всего.

Чему я научусь в уроке «Поиск строк без совпадений (антиобъединение)»?

Шаблон LEFT JOIN и IS NULL для поиска потерянных и отсутствующих данных. Ты практикуешь SQL Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать SQL Interview Prep?

Предыдущий опыт не требуется. SQL Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Поиск строк без совпадений (антиобъединение)»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке SQL Interview Prep?

Да. Каждый урок SQL Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. LEFT JOIN и сохранение несовпавших строк
  2. Смысл RIGHT и FULL OUTER JOIN
  3. Поиск строк без совпадений (антиобъединение)
  4. Ловушка WHERE при внешнем объединении
← Назад к SQL Interview Prep