Поиск строк без совпадений (антиобъединение)
Шаблон 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 kLEFT JOIN other o ON o.fk = k.idWHERE 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 — локальная установка не требуется.
Все уроки этого курса
- LEFT JOIN и сохранение несовпавших строк
- Смысл RIGHT и FULL OUTER JOIN
- Поиск строк без совпадений (антиобъединение)
- Ловушка WHERE при внешнем объединении