Что должен сохранить оптимизатор при преобразовании коррелированного EXISTS в полусоединение?
При преобразовании коррелированного EXISTS в полусоединение оптимизатор должен сохранить только факт существования хотя бы одной подходящей строки внутреннего источника. Каждая строка внешнего запроса должна попасть в результат не более одного раза, даже если внутри найдено несколько совпадений.
В реляционной алгебре полусоединение используется для проверки наличия связанных строк без присоединения их столбцов к результату. Такой подход решает типичную задачу фильтрации: оставить строки одной таблицы, для которых существует соответствие в другой.
SQL выражает эту логику через EXISTS. Оптимизатор может преобразовать декларативную проверку существования в более эффективный план полусоединения, не меняя логический результат запроса.
Обычное соединение может увеличить число строк внешнего источника: одна внешняя строка повторится для каждого совпадения во внутреннем источнике. Для EXISTS это неверно: наличие двух или ста совпадений должно иметь тот же результат, что и наличие одного.
Если оптимизатор преобразует EXISTS в обычное соединение без устранения дубликатов, результат станет некорректным. Добавление DISTINCT после такого соединения иногда исправляет результат, но может потребовать лишней сортировки или хеширования и не всегда эквивалентно исходной логике при сложных выражениях.
Логика коррелированного EXISTS такова: для каждой строки внешнего запроса вычисляется условие связи, после чего внутренний источник проверяется до нахождения первой подходящей строки. Значения столбцов внутреннего источника в результат не передаются; важен только булев результат проверки.
Полусоединение имеет ту же семантику. Оно возвращает строки только левой стороны и гарантирует, что каждая такая строка учитывается максимум один раз. В физическом плане это может быть реализовано через ранний выход после первого совпадения, хеш-полусоединение или другой способ, выбранный оптимизатором.
Минимальный пример:
Если у клиента несколько заказов, он всё равно возвращается одной строкой. Эквивалентность с обычным JOIN потребовала бы отдельно устранять дубликаты, поэтому JOIN не является прямой заменой EXISTS.
Преобразование называется декорреляцией: зависимость внутреннего запроса от текущей строки внешнего запроса представляется в плане как операция соединения. Оно допустимо только при сохранении исходной семантики. Наличие агрегатов, оконных функций, DISTINCT, LIMIT, пользовательских функций с побочными эффектами или иной чувствительности к порядку вычислений может ограничить такое преобразование.
Для EXISTS важна также трёхзначная логика SQL: если условие связи возвращает UNKNOWN из-за NULL, такая строка не считается найденным совпадением. При этом содержимое списка SELECT внутри EXISTS обычно не влияет на результат, поскольку проверяется только наличие строки.
В системе нужно выбрать клиентов, у которых есть хотя бы один заказ за заданный период. Разработчик заменил EXISTS на соединение таблиц клиентов и заказов, а затем получил повторяющиеся клиенты для заказчиков с несколькими заказами.
Рассматривались два варианта. Обычный JOIN с DISTINCT был понятен, но мог обрабатывать большое число строк заказов и затем удалять дубликаты. EXISTS явно выражал требование проверки наличия и позволял оптимизатору применить полусоединение с прекращением поиска после первого совпадения.
Выбрали EXISTS. Это сохранило требуемую кардинальность результата и дало оптимизатору возможность использовать индекс по ключу клиента и условию периода. Проверка плана выполнения подтвердила, что внутренние совпадения не материализуются как отдельные строки результата.
Можно ли всегда заменить EXISTS на INNER JOIN без изменения результата?
Нет. INNER JOIN возвращает отдельную строку для каждого совпадения, тогда как EXISTS возвращает внешнюю строку максимум один раз. Замена безопасна только при дополнительной гарантии уникальности внутреннего ключа либо при явном устранении дубликатов с сохранением всех остальных условий.
Влияет ли количество совпавших строк на значение EXISTS?
Нет. Для EXISTS различаются только два состояния: подходящая строка найдена или не найдена. Поэтому одна и тысяча совпадений логически дают одинаковый результат для конкретной внешней строки, хотя физическая стоимость поиска может различаться при отсутствии подходящего индекса или при необходимости проверить много строк до первого совпадения.
Всегда ли преобразование EXISTS в полусоединение ускоряет запрос?
Нет. Полусоединение обычно хорошо соответствует семантике проверки наличия, но итог зависит от статистики, индексов, размера источников и селективности условий. Оптимизатор может выбрать план с хешированием, сканированием или другим методом, а декорреляция может быть ограничена конструкциями, семантика которых зависит от порядка или числа вычислений.