Короткий ответ
При корректных условиях оба запроса дают одинаковый логический результат, но NOT EXISTS часто читается яснее по смыслу («не существует связанной записи») и в некоторых СУБД/версиях может эффективнее оптимизироваться, особенно при многостолбцовом условии соединения.
Как это работает подробнее
LEFT JOIN ... WHERE b.id IS NULL сначала строит полное соединение со всеми совпадениями и несовпадениями, а затем отфильтровывает только те строки, где совпадения не нашлось; NOT EXISTS (SELECT 1 FROM b WHERE b.a_id = a.id) для каждой строки a сразу проверяет наличие соответствия без необходимости материализовать полный JOIN.
Пример кода
question-64.sql
SELECT c.customer_id
FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);Что использовать на практике
- Практика. современные оптимизаторы PostgreSQL часто превращают оба варианта в похожий физический план (
Anti Join), поэтому разница в производительности не всегда значима — стоит сравниватьEXPLAIN ANALYZEна конкретных данных, а не полагаться на общее правило. Выбор между ними часто определяется читаемостью кода и условиями на множественные столбцы соединения, гдеNOT EXISTSс несколькими условиями в подзапросе часто компактнее. - Проверка. Проверка: сравнить результат с
SELECT c.customer_id FROM customers c LEFT JOIN orders o ON o.customer_id = c.customer_id WHERE o.customer_id IS NULL— наборыcustomer_idдолжны полностью совпасть.
Подводные камни
- Частая ошибка. при соединении по нескольким столбцам в
LEFT JOIN ... WHERE col IS NULLпроверяют наNULLтолько один из столбцов соединения, что может дать неверный результат, если именно этот столбец оказалсяNULLв найденной (а не отсутствующей) паре.