Как область видимости CTE ограничивает использование его имени за пределами оператора, в котором он объявлен?
CTE существует только в пределах одного SQL-оператора, начинающегося с соответствующего WITH. После завершения этого оператора имя CTE исчезает: его нельзя использовать в следующем операторе как имя таблицы или представления.
CTE задаёт именованный результат запроса, а не постоянный объект базы данных. Его область видимости обычно включает основной запрос и допустимые вложенные части этого же оператора.
CTE появился как способ дать имя промежуточному результату сложного запроса. Это решает проблему плохо читаемых вложенных подзапросов и позволяет последовательно описывать этапы преобразования данных.
При этом CTE не предназначен для создания долговременного набора данных. Для повторного использования результата между несколькими операторами применяются временные таблицы, обычные представления или материализованные представления.
Разработчик может ошибочно воспринимать CTE как временную таблицу, доступную до конца сессии или транзакции. Тогда первый оператор выполнится, а последующий завершится ошибкой о неизвестном отношении или таблице.
Такая ошибка особенно вероятна, когда несколько запросов визуально находятся рядом в одном скрипте. Точка с запятой завершает оператор, поэтому следующий оператор уже не наследует область видимости предыдущего CTE.
CTE связывает имя с запросом только внутри одного оператора. Например, после WITH recent AS (...) имя recent доступно основному запросу и, в зависимости от правил конкретной СУБД, допустимым вложенным запросам этого же оператора.
Первый SELECT использует CTE корректно. Второй SELECT является отдельным оператором и не видит имя recent; для него нужно заново объявить CTE или создать объект с более долгой областью жизни.
Несколько CTE внутри одного WITH могут образовывать цепочку: последующий CTE обычно может ссылаться на ранее объявленный. Возможность прямой ссылки на ещё не объявленный CTE и правила взаимных ссылок зависят от диалекта SQL; рекурсивные ссылки требуют специального синтаксиса рекурсивного CTE.
Вложенный запрос обычно может использовать CTE внешнего уровня, если это разрешено правилами конкретной СУБД. Если во вложенной области объявлено другое отношение с тем же именем, оно может скрыть внешнее имя; такие конструкции ухудшают читаемость и их лучше избегать.
Область видимости не определяет способ физического выполнения CTE. Оптимизатор может встроить его в основной план или материализовать согласно правилам и настройкам СУБД. Это отдельный вопрос от того, где имя CTE допустимо использовать.
Команда строит отчёт из трёх отдельных запросов: сначала формируется CTE с отфильтрованными заказами, затем его предполагают использовать в запросах для продаж, возвратов и среднего чека. Разработчик объединяет эти запросы точками с запятой и получает ошибку на втором запросе.
Вариант с копированием определения CTE в каждый запрос прост, но создаёт дублирование и риск расхождения условий. Временная таблица позволяет выполнить подготовку один раз и использовать результат несколькими операторами, однако требует управления жизненным циклом, места хранения и согласованностью данных.
Если набор нужен только этому скрипту и объём умеренный, выбранная схема — создать временную таблицу в рамках транзакции, заполнить её и выполнить три отчётных запроса. Если набор должен использоваться разными пользователями или запросами постоянно, более подходящим решением будет представление или материализованное представление.
Сохраняется ли CTE после завершения транзакции?
Нет. Обычно CTE не сохраняется даже до конца транзакции: его область видимости заканчивается вместе с оператором, в котором он объявлен. Транзакция управляет атомарностью изменений и видимостью данных, но не превращает CTE во временную таблицу.
Можно ли ссылаться на CTE, объявленный ниже в списке определений?
В общем случае нет: обычные CTE образуют последовательную область разрешения имён, поэтому последующий элемент может ссылаться на предыдущий, но не наоборот. Исключения связаны с рекурсивными конструкциями и конкретными правилами диалекта, поэтому переносить такое поведение между СУБД без проверки нельзя.
Что произойдёт, если вложенный запрос объявит CTE с тем же именем, что и внешний?
Внутренняя декларация может скрыть внешнюю, и ссылка внутри вложенного контекста будет разрешаться к внутреннему CTE. Это не переопределяет внешний CTE глобально: после выхода из вложенной области внешнее имя снова доступно. На практике одинаковые имена в разных уровнях лучше не использовать, поскольку они затрудняют ревью и могут изменить смысл запроса при последующем рефакторинге.