Перейти к основному содержанию
ClickHouse поддерживает общие табличные выражения (CTE), общие скалярные выражения и рекурсивные запросы.

Общие табличные выражения

Общие табличные выражения — это именованные подзапросы. На них можно ссылаться по имени в любом месте SELECT-запроса, где допускается табличное выражение. На именованные подзапросы можно ссылаться по имени в области видимости текущего запроса или в областях видимости дочерних подзапросов. Каждая ссылка на общее табличное выражение в SELECT-запросах всегда заменяется подзапросом из его определения, если CTE явно не определено как материализованное (см. Материализованные общие табличные выражения). Рекурсия предотвращается за счёт исключения текущего CTE из процесса разрешения идентификаторов. Обратите внимание, что CTE не гарантируют одинаковые результаты во всех местах, где к ним обращаются, поскольку для каждого случая использования запрос выполняется заново.

Синтаксис

Пример

Пример случая, когда подзапрос выполняется повторно:
Если бы CTE передавали именно результаты, а не просто фрагмент кода, вы бы всегда видели 1000000 Однако поскольку мы дважды обращаемся к cte_numbers, случайные числа каждый раз генерируются заново, и поэтому мы видим разные случайные результаты: 280501, 392454, 261636, 196227 и так далее…

Материализованные общие табличные выражения

По умолчанию ClickHouse подставляет подзапрос CTE во все места, где на него есть ссылка, и каждый раз выполняет его заново. Добавление ключевого слова MATERIALIZED указывает ClickHouse выполнить подзапрос CTE ровно один раз, сохранить результат во временной таблице и использовать эту таблицу для всех ссылок. Это особенно полезно, когда один и тот же CTE используется в запросе несколько раз (например, в self-join’ах или нескольких подзапросах IN), поскольку базовое вычисление выполняется только один раз.
Материализованные CTE — экспериментальная возможность. Для их использования должны быть включены анализатор и настройка enable_materialized_cte.

Синтаксис

Когда использовать

Материализованные CTE наиболее полезны в следующих случаях:
  • Один и тот же CTE используется в запросе более одного раза. Без MATERIALIZED каждая ссылка на него повторно выполняет подзапрос независимо.
  • CTE содержит недетерминированные функции, такие как generateRandom. Материализация гарантирует, что все ссылки будут видеть одни и те же данные.
  • CTE включает ресурсоёмкие вычисления (агрегации, JOIN, сканирование больших объёмов данных), которые не следует выполнять повторно.
Если материализованный CTE используется только один раз, ClickHouse автоматически разворачивает его обратно в обычный подзапрос, чтобы избежать лишних накладных расходов.

Примеры

Пример 1: JOIN материализованного CTE с самим собой Без MATERIALIZED обе стороны JOIN выполняли бы подзапрос независимо. С MATERIALIZED таблица сканируется один раз, и обе стороны JOIN читают из одной и той же временной таблицы.
Пример 2: Детерминированные результаты при недетерминированных функциях Обычные CTE с generateRandom дают разные результаты при каждом обращении. Материализация CTE обеспечивает согласованность:
Поскольку обе ссылки обращаются к одним и тем же материализованным данным, результат всегда равен 1000000. Пример 3: Цепочка материализованных CTE Материализованные CTE могут ссылаться на другие материализованные CTE. ClickHouse определяет зависимости и материализует их в правильном порядке:
Порядок определений CTE не имеет значения — допускаются ссылки вперёд:

Ограничения

  • Требуется экспериментальная настройка: настройка enable_materialized_cte должна быть включена.
  • Требуется анализатор: материализованные CTE работают только при включенном анализаторе (enable_analyzer = 1).
  • Не поддерживается с RECURSIVE: использование ключевых слов MATERIALIZED и RECURSIVE вместе не допускается и приводит к исключению UNSUPPORTED_METHOD.
  • Коррелированные CTE запрещены: материализованный CTE не может ссылаться на столбцы из внешних областей видимости запроса.

Общие скалярные выражения

ClickHouse позволяет объявлять псевдонимы для произвольных скалярных выражений в конструкции WITH. На общие скалярные выражения можно ссылаться в любом месте запроса.
Если общее скалярное выражение ссылается не на константный литерал, выражение может приводить к появлению свободных переменных. ClickHouse разрешает любой идентификатор в ближайшей возможной области видимости, а значит, при конфликтах имен свободные переменные могут ссылаться на неожиданные сущности или приводить к коррелированному подзапросу. Рекомендуется определять CSE как лямбда-функцию (это возможно только при включенном анализаторе), связывая все используемые идентификаторы, чтобы добиться более предсказуемого разрешения идентификаторов в выражениях.

Синтаксис

Примеры

Пример 1: Использование константного выражения как “переменной”
Пример 2: Использование функций высшего порядка для ограничения идентификаторов
Пример 3: Использование функций высшего порядка со свободными переменными Следующие запросы показывают, что непривязанные идентификаторы разрешаются как сущность из ближайшей области видимости. Здесь extension не привязана в теле лямбда-функции gen_name. Хотя extension определена как '.txt' в виде общего скалярного выражения в области видимости определения и использования generated_names, она разрешается как столбец таблицы extension_list, поскольку доступна в подзапросе generated_names.
Пример 4: Исключение результата выражения sum(bytes) из списка столбцов в предложении SELECT
Пример 5: Использование результатов скалярного подзапроса
Пример 6: Повторное использование выражения в подзапросе

Рекурсивные запросы

Необязательный модификатор RECURSIVE позволяет запросу WITH обращаться к собственному результату. Пример: Пример: Суммирование целых чисел от 1 до 100
Рекурсивные CTE опираются на анализатор запросов, добавленный в версии 24.3. Если вы используете версию 24.3+ и сталкиваетесь с исключением (UNKNOWN_TABLE) или (UNSUPPORTED_METHOD), это означает, что анализатор отключен для вашего экземпляра, роли или профиля. Чтобы включить анализатор, активируйте настройку allow_experimental_analyzer или обновите настройку compatibility до более новой версии. Начиная с версии 24.8, анализатор был полностью переведен в промышленную эксплуатацию, а настройка allow_experimental_analyzer была переименована в enable_analyzer.
Общая форма рекурсивного запроса WITH всегда состоит из нерекурсивного выражения, затем UNION ALL, затем рекурсивного выражения, при этом только рекурсивное выражение может содержать ссылку на собственный результат запроса. Рекурсивный CTE-запрос выполняется следующим образом:
  1. Вычислите нерекурсивное выражение. Поместите результат запроса нерекурсивного выражения во временную рабочую таблицу.
  2. Пока рабочая таблица не пуста, повторяйте следующие шаги:
    1. Вычислите рекурсивное выражение, подставив текущее содержимое рабочей таблицы вместо рекурсивной самоссылки. Поместите результат запроса рекурсивного выражения во временную промежуточную таблицу.
    2. Замените содержимое рабочей таблицы содержимым промежуточной таблицы, затем очистите промежуточную таблицу.
Рекурсивные запросы обычно используются для работы с иерархическими или древовидными данными. Например, можно написать запрос, выполняющий обход дерева: Пример: Обход дерева Сначала создадим таблицу дерева:
Это дерево можно обойти с помощью такого запроса: Пример: Обход дерева

Порядок обхода

Чтобы задать порядок обхода в глубину, для каждой результирующей строки мы вычисляем массив строк, которые уже были посещены: Пример: Обход дерева в глубину
Чтобы получить порядок обхода в ширину, обычно добавляют столбец, отслеживающий глубину поиска: Пример: Обход дерева в ширину

Обнаружение циклов

Сначала создадим таблицу графа:
Мы можем обойти этот граф с помощью следующего запроса: Пример: Обход графа без обнаружения циклов
Но если мы добавим в этот граф цикл, предыдущий запрос завершится ошибкой Maximum recursive CTE evaluation depth:
Стандартный способ обработки циклов — сформировать массив уже посещённых узлов: Пример: Обход графа с обнаружением циклов

Бесконечные запросы

Можно также использовать бесконечные рекурсивные CTE-запросы, если во внешнем запросе указан LIMIT: Пример: Бесконечный рекурсивный CTE-запрос

Завершающая запятая

Запятая допускается после последнего элемента конструкции WITH:
Последнее изменение 2 июля 2026 г.