EXISTS

EXISTS проверяет, вернул ли подзапрос хотя бы одну строку. Значения выбранных столбцов не важны.

SELECT u.id, u.email
FROM users AS u
WHERE EXISTS (
    SELECT 1
    FROM orders AS o
    WHERE o.user_id = u.id
      AND o.state = 'PAID'
);

Это естественная запись вопроса: «существует ли оплаченный заказ пользователя?». Логически СУБД может остановить поиск на первой найденной строке и построить semi join.

NOT EXISTS

SELECT u.id, u.email
FROM users AS u
WHERE NOT EXISTS (
    SELECT 1
    FROM orders AS o
    WHERE o.user_id = u.id
);

Так ищут пользователей без заказов. В отличие от NOT IN, наличие NULL в нерелевантном столбце подзапроса не ломает логику.

EXISTS или JOIN

Если от правой таблицы не нужны столбцы, EXISTS точнее выражает намерение и не размножает левые строки. JOIN удобнее, когда данные обеих сторон действительно нужны.

Поддерживающий индекс для примера:

CREATE INDEX idx_orders_user_state
    ON orders (user_id, state);

Ловушки

  • Некоррелированный EXISTS либо пропустит все внешние строки, либо ни одной.
  • SELECT 1 — соглашение для читаемости; SELECT * обычно не заставляет читать все столбцы.
  • Производительность определяется не словом EXISTS, а доступом к данным и итоговым планом.

Связано: Подзапросы с несколькими строками и одним столбцом, Индексы SQL, Что лучше подзапросы или JOIN.

Вопросы

Что возвращает EXISTS? Логическое значение наличия хотя бы одной строки.

Зачем внутри пишут SELECT 1? Чтобы показать: содержимое строки не используется.

Почему EXISTS полезен вместо JOIN? Он не создает дубликаты левой строки и явно выражает проверку существования.