Короткий ответ
IN сравнивает значение с готовым списком результатов подзапроса, а EXISTS просто проверяет, вернул ли коррелированный подзапрос хотя бы одну строку, без интереса к конкретным значениям — для простых случаев они дают одинаковый результат, но ведут себя по-разному при NULL и в сложных подзапросах.
Как это работает подробнее
WHERE x IN (SELECT y FROM t) требует, чтобы x совпало с одним из значений y; если подзапрос — коррелированный (обращается к внешней таблице), EXISTS просто возвращает TRUE/FALSE по факту существования подходящей строки, что для СУБД часто проще оптимизировать, особенно если внутри нет необходимости строить весь список значений.
Пример кода
Запрос SQL
SELECT c.customer_id
FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);Что использовать на практике
- На практике:
EXISTSобычно предпочтительнее, когда подзапрос коррелированный и таблицы большие, потому что движок может остановиться на первой найденной подходящей строке.INудобнее для короткого, явного списка значений или независимого подзапроса. - Результат: Результат — клиенты, у которых есть хотя бы один заказ.
- Проверка: количество строк должно совпасть с
SELECT COUNT(DISTINCT customer_id) FROM orders.
Подводные камни
- Частая ошибка: используют
NOT IN (SELECT ... ), где подзапрос может вернутьNULL-значения — это приводит к тому, что весьNOT INнеожиданно не находит ни одной строки;NOT EXISTSэтой проблемы не имеет, поэтому для «отсутствия записи» он надёжнее.