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? Он не создает дубликаты левой строки и явно выражает проверку существования.