Все материалы
Продуктовая аналитикапрактикумсредний

IN и EXISTS в SQL: как проверять принадлежность и наличие связанных строк

Сравнение IN и EXISTS в SQL для аналитики: фильтрация по списку, проверка событий пользователя, NULL и выбор читаемого решения.

КПКейсПрактика27 июля 2026 г.8 мин

Нужно выбрать пользователей, которые купили товар из категории electronics, или заказы, для которых есть возврат. IN выглядит коротко, EXISTS — чуть сложнее, но подчёркивает вопрос «существует ли хотя бы одна строка». Неправильный выбор часто связан не со скоростью, а с NULL, дублями и смыслом связи. Разберём вопрос на одном запросе: Когда использовать IN, а когда EXISTS для проверки связанной записи?

Когда нужен этот приём

Когда использовать IN, а когда EXISTS для проверки связанной записи?

Для списка из нескольких статичных тарифов IN читается естественно. Для проверки события оплаты у пользователя EXISTS показывает намерение и не размножает пользователя при десяти платежах. Если подзапрос IN возвращает NULL, логика NOT IN может дать неожиданный UNKNOWN и убрать все строки.

проверяем наличие, а не размножаем строки

IN подходит для понятного небольшого списка. EXISTS — для факта связанной строки. Для отрицательной проверки предпочитай NOT EXISTS, если в данных возможен NULL. Скорость подтверждай EXPLAIN ANALYZE на реальном объёме.

Логика оператора

IN сравнивает значение с набором значений. EXISTS проверяет, возвращает ли коррелированный подзапрос хотя бы одну строку и может остановиться после первого совпадения. Современный оптимизатор иногда превращает их в похожий semi-join, поэтому не стоит обещать, что один оператор всегда быстрее.

Определи, сравниваешь ли ты значение со списком или проверяешь связь между сущностями. Если связь one-to-many и нужна только проверка факта, начни с EXISTS. Для NOT EXISTS отдельно протестируй строки с NULL и пустые наборы.

  • Зафиксируй одну строку результата.
  • Отдели условия по исходным строкам от условий по рассчитанным значениям.
  • Назови период, ключи связи и правило для повторов.

Рабочая версия

Названия таблиц здесь условные. При переносе сохрани зерно и бизнес-условия, а не буквальный синтаксис примера.

Пользователи, для которых существует оплаченный заказ
SELECT u.user_id
FROM users u
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.user_id = u.user_id
    AND o.status = 'paid'
    AND o.paid_at >= DATE '2026-07-01'
);

Как сверить ответ

Собери пять-десять строк, где ответ можно получить вручную. Добавь NULL, дубль, объект без связанной записи и значение на границе периода. Затем сравни число строк и уникальных ключей до и после спорного шага.

Выполненный без ошибки запрос ещё не является правильным. Другой аналитик должен суметь восстановить из него источник, фильтр, grain и ожидаемое поведение на крайних случаях.

Выбор оператора
ВопросОператорКомментарий
значение входит в списокINдля короткого набора
связь существуетEXISTSне размножает строки
связи нетNOT EXISTSустойчивее к NULL
нужны поля второй таблицыJOINдобавляет атрибуты

Когда выбрать другой способ

NOT IN с NULL — классическая ловушка трёхзначной логики. IN (subquery) не означает JOIN и не даёт колонки второй таблицы. EXISTS не возвращает поля подзапроса. Не делай SELECT DISTINCT после JOIN только для имитации EXISTS.

IN подходит для понятного небольшого списка. EXISTS — для факта связанной строки. Для отрицательной проверки предпочитай NOT EXISTS, если в данных возможен NULL. Скорость подтверждай EXPLAIN ANALYZE на реальном объёме.

Semi-join оставляет пользователя один раз

Десять платежей одного пользователя не создают десять строк результата.

Строк

Небольшой эксперимент

Напиши два отчёта: пользователи с оплатой и пользователи без оплаты. Сгенерируй случай с двумя оплатами и NULL в справочнике. Сравни IN, EXISTS, NOT IN и NOT EXISTS и объясни результат словами, а не только ссылкой на план.

  • Сначала запиши ожидаемый результат словами.
  • Проверь запрос на маленькой контрольной выборке.
  • Объясни, что запрос считает и чего не доказывает.

Следующий шаг

IN подходит для понятного небольшого списка. EXISTS — для факта связанной строки. Для отрицательной проверки предпочитай NOT EXISTS, если в данных возможен NULL. Скорость подтверждай EXPLAIN ANALYZE на реальном объёме.

Продолжить чтение
Вся библиотека