Как разрешается ссылка на столбец в подзапросе, если такое имя есть и внутри, и во внешнем запросе?
Ссылка разрешается в ближайшей области видимости: если столбец с таким именем есть во внутреннем запросе, используется он, а внешний столбец не считается корреляцией. Чтобы явно обратиться к внешнему уровню, нужно квалифицировать столбец псевдонимом внешней таблицы.
Коррелированные подзапросы позволяют внутреннему запросу использовать значения текущей строки внешнего запроса. Для этого SQL рассматривает каждый подзапрос как отдельный блок с собственной областью видимости и при разрешении имени сначала ищет столбец во внутреннем блоке, затем во внешних.
Такое правило позволяет внутреннему запросу переиспользовать имена столбцов без обязательного переименования. Обратная сторона — неявная корреляция может возникнуть случайно или исчезнуть после изменения схемы.
Неоднозначные ссылки без квалификаторов опасны: запрос может успешно выполниться, но проверять не связь с внешней строкой, а условие между двумя столбцами внутреннего источника. Особенно рискованны изменения схемы, при которых во внутренней таблице появляется столбец с именем, ранее доступным только из внешнего запроса.
Например, разработчик может ожидать проверку принадлежности заказа текущему сотруднику, но фактически получить сравнение столбца заказа с самим собой. В результате EXISTS способен вернуть истину для каждой внешней строки, если во внутренней таблице есть хотя бы одна подходящая ненулевая строка.
Разрешение имени обычно происходит так:
Пример безопасной записи:
Здесь o.employee_id однозначно относится к внутренней таблице, а e.id — к внешней. Подзапрос вычисляется в контексте каждой строки employees, поэтому это коррелированный EXISTS.
Если вместо этого написать сравнение двух неуточнённых ссылок с одинаковым именем, обе ссылки могут разрешиться как o.employee_id. Тогда корреляция отсутствует, а условие фактически сравнивает столбец с самим собой. При NULL результат такого сравнения не равен TRUE, поэтому поведение зависит от наличия ненулевых значений.
Квалификация псевдонимами важна не только для читаемости. Она фиксирует намерение запроса, защищает от скрытого изменения смысла при расширении схемы и облегчает преобразование коррелированного подзапроса в соединение или полусоединение оптимизатором. Само такое преобразование не меняет правила разрешения имён: сначала определяется смысл ссылок, затем выбирается физический план.
В отчёте нужно вывести сотрудников, у которых есть хотя бы один заказ. В первом варианте разработчик использует неуточнённое имя employee_id внутри подзапроса. Запрос выполняется, но после появления одноимённого столбца во внутренней таблице условие перестаёт связывать заказ с текущим сотрудником.
Возможный вариант — оставить имена неуточнёнными. Его плюс — краткость, но минусы критичны: зависимость от схемы, трудная проверка и риск незаметной потери корреляции. Второй вариант — явно квалифицировать все ссылки псевдонимами таблиц; он немного длиннее, зато семантика сохраняется при рефакторинге.
Выбирается второй вариант. В результате запрос проверяет именно существование заказа текущего сотрудника, а ревьюер и оптимизатор могут однозначно распознать корреляцию.
1. Может ли оптимизатор преобразовать корректно записанный коррелированный EXISTS в соединение?
Да, если такое преобразование сохраняет семантику. Часто EXISTS преобразуется в полусоединение, которое проверяет наличие совпадения, но не размножает строки внешнего запроса. Разрешение имён происходит до выбора плана, поэтому оптимизатор не вправе трактовать внешнюю ссылку иначе.
2. Что произойдёт, если внутренний запрос не содержит столбца с указанным именем?
Тогда ссылка может разрешиться во внешнем запросе и сделать подзапрос коррелированным. Это допустимо, но не всегда очевидно из текста запроса. Поэтому внешние ссылки следует явно квалифицировать псевдонимом внешней таблицы, даже когда сейчас одноимённого внутреннего столбца нет.
3. Устраняет ли псевдоним таблицы риск случайной корреляции полностью?
Нет, если часть ссылок всё ещё оставлена без квалификатора. Псевдоним защищает только те обращения, где он явно указан. Надёжный стиль — квалифицировать столбцы всех участвующих таблиц, особенно в условиях подзапросов, JOIN и многоуровневых запросов.