Антисоединения и полусоединения (NOT EXISTS)
Находите «строки в A без совпадений в B» (антисоединение) и «строки в A хотя бы с одним совпадением в B» (полусоединение) с помощью EXISTS/NOT EXISTS
«Антисоединения и полусоединения (NOT EXISTS)» — бесплатный урок SQL Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Academy содержит 4 уроков всего.
Полусоединение: «есть хотя бы одно совпадение»
Верните строки из A, для которых существует хотя бы одна совпадающая строка в B, но только столбцы A. SQL реализует полусоединения с помощью EXISTS или IN.
Полусоединение с EXISTS
Пользователи, разместившие хотя бы один заказ:
SELECT u.* FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);Полусоединение с IN
Тот же результат, другой стиль:
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders);Почему EXISTS часто эффективнее
EXISTS применяет ранний выход: останавливается после первого совпадения для каждой внешней строки. IN может материализовать весь внутренний набор. Современные планировщики часто оптимизируют их до одного и того же плана, но EXISTS надёжнее использовать для огромных внутренних наборов.
Антисоединение: «нет совпадения»
Пользователи, которые NEVER размещали заказ, — три идиоматичных варианта:
-- NOT EXISTS (preferred):
SELECT u.* FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);
-- LEFT JOIN ... IS NULL:
SELECT u.* FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE o.id IS NULL;
-- NOT IN (risky with NULLs):
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders);Почему NOT IN рискован
Если внутренний подзапрос содержит NULL, NOT IN возвращает NULL, а WHERE отбрасывает строки, для которых предикат равен NULL. Результат: ноль строк. У NOT EXISTS такой проблемы нет.
Оптимизация планировщиком
Современный PostgreSQL распознаёт EXISTS и NOT EXISTS как шаблоны полусоединения и антисоединения и может выполнять их с помощью хеш-соединений:
EXPLAIN ANALYZE
SELECT u.* FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
-- → Hash Anti JoinАнтисоединения по нескольким столбцам
Используйте составные ключи:
SELECT * FROM order_items oi
WHERE NOT EXISTS (
SELECT 1 FROM shipments s
WHERE s.order_id = oi.order_id
AND s.line_no = oi.line_no
);Полусоединение с EXISTS и дополнительным предикатом
Добавьте фильтры во внутренний подзапрос:
SELECT u.* FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id
AND o.status = 'paid'
AND o.created_at >= NOW() - INTERVAL '30 days'
);Индексируйте столбец корреляции
И в EXISTS, и в NOT EXISTS внутренний подзапрос фильтруется по ключу внешней строки. Без индекса по этому ключу выполняется сканирование для каждой внешней строки:
CREATE INDEX orders_user_id_idx ON orders(user_id);Когда использовать LEFT JOIN ... IS NULL
Для панелей мониторинга, которым нужны также столбцы из несопоставленной стороны, естественно подходит стиль с LEFT JOIN. Для чистой семантики антисоединения понятнее использовать NOT EXISTS.
За пределами SQL: фильтры Блума
Для огромных антисоединений может помочь предварительная фильтрация с помощью фильтра Блума. PostgreSQL поддерживает индексы bloom через расширение bloom.
Итоги
Полусоединение = «есть совпадение»; антисоединение = «нет совпадения».
- EXISTS для полусоединения
- NOT EXISTS для антисоединения, безопасного при NULL
- Индексируйте столбец корреляции
- Избегайте NOT IN, если внутренний набор может содержать NULL
Быстрая проверка
Вам нужны пользователи, которые NEVER размещали заказ. Какой вариант SQL самый безопасный и идиоматичный?
Часто задаваемые вопросы
Урок «Антисоединения и полусоединения (NOT EXISTS)» бесплатный?
Да — полный текст урока «Антисоединения и полусоединения (NOT EXISTS)» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Academy, подпишись на CoddyKit PRO. Курс SQL Academy содержит 4 уроков всего.
Чему я научусь в уроке «Антисоединения и полусоединения (NOT EXISTS)»?
Находите «строки в A без совпадений в B» (антисоединение) и «строки в A хотя бы с одним совпадением в B» (полусоединение) с помощью EXISTS/NOT EXISTS Ты практикуешь SQL Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать SQL Academy?
Предыдущий опыт не требуется. SQL Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Антисоединения и полусоединения (NOT EXISTS)»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке SQL Academy?
Да. Каждый урок SQL Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Перекрёстные соединения и декартовы произведения
- Латеральные соединения (LATERAL JOIN)
- Антисоединения и полусоединения (NOT EXISTS)
- Оптимизация производительности соединений нескольких таблиц