Два запроса ищут пользователей без заказов: LEFT JOIN orders ON ... WHERE orders.id IS NULL и WHERE NOT EXISTS (SELECT 1 FROM orders WHERE ...). Что верно о производительности в PostgreSQL?
AВ PostgreSQL оптимизатор обычно приводит оба варианта к одному плану — Anti Join, и производительность одинакова
BВариант с
LEFT JOIN + IS NULL всегда быстрее, потому что NOT EXISTS выполняет подзапрос для каждой строкиCВариант с
NOT EXISTS всегда быстрее, потому что он прекращает поиск при первом совпадении в подзапросеDОба варианта выполняются последовательно — сначала полный
JOIN, потом фильтрация по NULLПравильный ответ. Оптимизатор PostgreSQL обычно распознаёт оба паттерна как анти-соединение и строит одинаковый план выполнения — разница в скорости минимальна.
Разбор
Современные оптимизаторы (PostgreSQL, SQL Server, Oracle) умеют преобразовывать LEFT JOIN + IS NULL, NOT EXISTS и даже NOT IN (без NULL) в один оператор Anti Join. В EXPLAIN это видно как Hash Anti Join или Merge Anti Join. На практике для PostgreSQL разница в скорости между первыми двумя подходами пренебрежимо мала. NOT IN может проиграть из-за обработки NULL. Рекомендация: выбирать наиболее читаемый вариант.
Проверь себя · 1/3разбор после ответа
Нужно построить отчёт: по каждому продукту и каждому дню месяца — сумма продаж, включая дни с нулевыми продажами. Как сформировать каркас из всех пар дата-продукт?
Ещё вопросы по теме «JOIN и операции множеств»
- В отчёте нужно вывести всех пользователей и количество их заказов, включая тех, у кого заказов нет. Какой тип соединения между `users` и `orders` по `user_id` нужен?
- Нужно посчитать число пользователей, которые сделали хотя бы 1 заказ (таблицы `users(user_id)` и `orders(user_id, order_id)`). Какой запрос посчитает правильно?
- Вы соединили `orders` с `order_items` и `payments` по `order_id`, а затем посчитали `SUM(paid_amount)`. Сумма оказалась завышенной. Что вероятнее всего произошло и как исправить?
- Есть две таблицы с одинаковой схемой: `events_web(user_id, event_name, created_at)` и `events_app(user_id, event_name, created_at)`. Нужно получить общий поток событий для дальнейшей агрегации. Что использовать?
- Нужно получить уникальный список `user_id`, которые пришли из двух каналов: `campaign_a(user_id)` и `campaign_b(user_id)`. Как корректнее объединить списки, чтобы убрать дубликаты?
- Все вопросы по «JOIN и операции множеств» →