5

Перенос условий JOIN ON в WHERE в PostgreSQL

Не всегда оптимизация оптимизирует

Не всегда оптимизация оптимизирует

В подавляющем большинстве случаев современный планировщик PostgreSQL обработает условия в JOIN ... ON и в WHERE абсолютно идентично. Оба условия будут участвовать в формировании плана выполнения, и никакой разницы в производительности не будет.

Однако, есть несколько важных нюансов и сценариев, где перенос условий из ON в WHERE может быть полезен или, наоборот, критически важен.

1. Читаемость и однозначность запроса (Самый главный положительный эффект)

Это не прямое последствие для СУБД, а скорее для разработчика, но оно косвенно влияет на качество кода и меньшее количество ошибок.

  • WHERE — это финальное фильтрующее условие. Оно четко указывает, какие строки должны попасть в окончательный результат.

  • ON — это условие связи таблиц. Оно определяет, как строки двух таблиц соотносятся друг с другом.

Разделяя логику связи (ON) и логику фильтрации (WHERE), вы делаете запрос более понятным и легким для поддержки.

Пример:
Предположим, нам нужны все заказы и информация о клиентах, но только для клиентов из Москвы.

-- Менее читаемо (условие связи и фильтрации перемешаны)

SELECT *

FROM orders o

LEFT JOIN customers c ON o.customer_id = c.id AND c.city = 'Москва';

-- Более читаемо и логично

SELECT *

FROM orders o

LEFT JOIN customers c ON o.customer_id = c.id

WHERE c.city = 'Москва'; -- Но ВНИМАНИЕ! Этот запрос не эквивалентен предыдущему!

Здесь ключевой момент: Эти два запроса не эквивалентны. Второй запрос превратит LEFT JOIN в INNER JOIN, потому что условие на таблицу customers в WHERE отфильтрует все строки, где c.city IS NULL (т.е. все "не совпавшие" строки от LEFT JOIN).

2. Потенциальная помощь планировщику в сложных запросах

Хотя планировщик очень умный, в исключительно сложных запросах с множеством JOIN и подзапросов, явное указание финального условия отбора в WHERE может упростить процесс построения плана. Это не гарантированное ускорение, но упрощение логики для планировщика.

  • Что делает планировщик? Он пытается "протолкнуть" условия (push down predicates) как можно ближе к сканированию данных.

  • Разница: Условие в ON должно быть применено во время операции соединения, а условие в WHERE — после него. Иногда явное разделение позволяет планировщику применить фильтр на более раннем этапе, если это выгодно.

На практике эта разница почти всегда нивелируется мощностью планировщика PostgreSQL.

3. Явное указание логики для OUTER JOIN (самая важная практическая разница)

Это обратная сторона медали. Перенос условия из ON в WHERE для LEFT|RIGHT|FULL JOIN кардинально меняет результат запроса.

  • Условие в ON: Фильтрует строки из правой таблицы до того, как будет выполнен JOIN. Строки из левой таблицы остаются, даже если условие не выполнилось.

  • Условие в WHERE: Фильтрует результат после того, как JOIN был выполнен. Для LEFT JOIN это означает, что все строки, где правое поле — NULL (из-за несовпадения), будут отброшены, превращая join по сути во INNER.

Положительным последствием здесь является то, что использование WHERE позволяет вам явно и однозначно указать, что вы хотите видеть только пересекающиеся строки.

-- Хочу всех клиентов и их заказы, но только те заказы, которые > 1000

SELECT *

FROM customers c

LEFT JOIN orders o ON c.id = o.customer_id AND o.amount > 1000;

-- Вернет ВСЕХ клиентов. Для тех, у кого нет больших заказов, поля заказа будут NULL.

-- Хочу только тех клиентов, у КОТОРЫХ ЕСТЬ заказы > 1000

SELECT *

FROM customers c

LEFT JOIN orders o ON c.id = o.customer_id

WHERE o.amount > 1000;

-- Это, по факту, INNER JOIN по условию на заказ. Не вернет клиентов без подходящих заказов.

Выводы

  1. Для INNER JOIN смело переносите условия из ON в WHERE. Разницы в производительности не будет, но запрос станет чище и понятнее. Планировщик обработает их одинаково.

  2. Для OUTER JOIN (LEFT, RIGHT, FULL) понимайте разницу. Перенос условия из ON в WHERE полностью меняет логику запроса. Это не оптимизация, а изменение результата.
    ON: "Как соединять таблицы?"
    WHERE: "Что показывать в финальном результате?"

  3. Положительные последствия переноса в WHERE — это в первую очередь:
    Улучшенная читаемость и сопровождаемость кода.
    Более явное и четкое выражение вашего намерения.
    В редких сложных случаях — потенциально более простой план запроса для СУБД.

Таким образом, главный положительный эффект — смысловой и архитектурный, а не прямой прирост производительности в современных версиях PostgreSQL.

Postgres DBA

157 постов27 подписчиков

Правила сообщества

Пока действуют стандартные правила Пикабу.