CodeRang
Темы SQL

EXISTS SQL и NOT EXISTS: как проверять наличие строк

EXISTS SQL проверяет, существует ли хотя бы одна строка, подходящая под условия подзапроса, а NOT EXISTS инвертирует эту проверку. На примере разберём, как найти записи без связанной пары и почему этот шаблон называют антиджойном.

Обновлено · Команда SQL Arena

Что потренируете

  • Находить строки без соответствия через NOT EXISTS
  • Понимать коррелированное условие во внутреннем запросе
  • Переписывать антиджойн через LEFT JOIN ... IS NULL
  • Избегать ловушки NOT IN при наличии NULL
  • Проверять отсутствие записей без лишней агрегации

Пример задачи в тренажёре

Так выглядит задача в SQL Arena: условие, редактор, результат и схема базы.

Пред.След.Случайная
ЗаданиеОтправкиAI Mentor
Задача: клиенты без заказов

Нужно вывести клиентов из customers, для которых в orders нет ни одной строки с тем же customer_id. В результате должны быть только customer_name и city.

Запрос 1PostgreSQLv16
SELECT c.customer_name,
       c.city
FROM customers c
WHERE NOT EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.id
)
ORDER BY c.customer_name;
Отправить
Результатcustomersorders
Ответ принят

Как работают EXISTS SQL и NOT EXISTS

Внешний запрос читает customers по одной строке. Для каждой такой строки PostgreSQL проверяет подзапрос внутри NOT EXISTS. Связь задаётся условием o.customer_id = c.id: оно спрашивает, существует ли хотя бы один заказ текущего клиента. Само значение в SELECT 1 не важно — оператору EXISTS нужен только факт наличия строки, а не её содержимое.

Если заказ найден, EXISTS становится истинным, а NOT EXISTS — ложным, поэтому клиент исключается. Если подходящего заказа нет, подзапрос не возвращает строк, NOT EXISTS становится истинным, и клиент остаётся. В данных примера заказы есть у Ava, Cara, Emma и Grace, поэтому результат содержит Ben, Dylan и Felix.

Ту же задачу можно решить через LEFT JOIN orders ... WHERE o.id IS NULL. Это тоже корректный антиджойн: сначала сохраняются все клиенты, а затем остаются только те, которым не присоединился заказ. Но NOT EXISTS часто точнее выражает смысл «не существует связанной строки». В отличие от NOT IN, он не превращает всю проверку в неопределённость из-за случайного NULL в наборе сравнения.

EXISTS возвращает TRUE, если вложенный запрос нашёл хотя бы одну строку, и FALSE, если не нашёл ни одной. Содержимое SELECT внутри такого подзапроса обычно несущественно: конструкции SELECT 1 и SELECT * выражают одну и ту же проверку существования. NOT EXISTS меняет результат на противоположный и поэтому подходит для поиска строк без соответствия в другой таблице. Корреляция обычно задаётся условием, которое связывает внешний идентификатор с внешним ключом во вложенном запросе.

SQLPostgreSQL v16
SELECT c.customer_name,
       c.city
FROM customers c
WHERE NOT EXISTS (
  SELECT 1 FROM orders o
  WHERE o.customer_id = c.id
)
ORDER BY c.customer_name;
Результат запроса
customer_namecity
BenYork
DylanLeeds
FelixBath

Ошибки при использовании EXISTS и NOT EXISTS SQL

01

Забыть связать подзапрос с внешней строкой

Если убрать o.customer_id = c.id, подзапрос проверит наличие любых заказов вообще. Пока таблица orders не пустая, NOT EXISTS станет ложным для каждого клиента, и результат окажется пустым.

02

Использовать NOT IN без учёта NULL

Выражение c.id NOT IN (SELECT customer_id FROM orders) может дать неожиданный результат, если подзапрос вернёт хотя бы один NULL. В трёхзначной логике SQL сравнение становится UNKNOWN, поэтому безопаснее явно контролировать NULL или использовать NOT EXISTS.

03

Проверять nullable-колонку после LEFT JOIN

В варианте LEFT JOIN ... IS NULL лучше проверять колонку правой таблицы, которая гарантированно не бывает NULL, например первичный ключ o.id. Проверка nullable-поля может смешать «строка не нашлась» и «строка нашлась, но значение пустое».

Задачи по теме в тренажёре

Трек «SQL для тестировщика»

После базового примера полезно менять предметную область, не меняя саму идею. Задачи «Товары без единой продажи» и «Книги без доступных экземпляров» учат видеть отсутствие связи, а более сложные варианты добавляют фильтр по периоду или условию: например, найти книги, которые не выдавались в определённом году. Для тестировщика этот паттерн особенно полезен при поиске «осиротевших» данных и проверке целостности, поэтому дальше логично перейти в трек «SQL для тестировщика». В SQL Arena есть 1 192 задачи и автоматическая проверка на скрытых данных.

Частые вопросы

Что такое EXISTS SQL?

EXISTS — оператор, который проверяет, возвращает ли подзапрос хотя бы одну строку. Если подходящая строка существует, условие EXISTS считается истинным.

Как работает EXISTS SQL?

Обычно подзапрос внутри EXISTS связывают с текущей строкой внешнего запроса через условие корреляции. Для результата важно только наличие строки, а не конкретное значение, указанное после SELECT.

Что такое NOT EXISTS SQL?

NOT EXISTS возвращает истину, когда подзапрос не находит ни одной подходящей строки. Его часто используют для поиска клиентов без заказов, товаров без продаж и других объектов без связанной записи.

Чем EXISTS SQL отличается от NOT EXISTS SQL?

EXISTS оставляет внешнюю строку, если соответствие найдено. NOT EXISTS делает обратное: условие истинно только тогда, когда соответствующей строки нет.

Чем NOT EXISTS отличается от LEFT JOIN в SQL?

Антиджойн можно выразить как через NOT EXISTS, так и через LEFT JOIN с последующей проверкой NULL со стороны присоединённой таблицы. NOT EXISTS часто прямо отражает смысл условия «не существует связанной строки» и не требует выводить столбцы второй таблицы.

Практика EXISTS SQL и антиджойнов

Решите задачи про отсутствующие связи в SQL Arena: клиенты без заказов, товары без продаж и другие варианты. На бесплатном тарифе доступны 292 задачи во всех четырёх диалектах без карты.

Практиковать NOT EXISTS