24.3 анализатор включен по умолчанию.
Подробнее о принципах его работы можно прочитать здесь.
Начиная с версии 26.9 анализатор обязателен: настройка enable_analyzer устарела, попытка задать ей значение 0 отклоняется, а анализ запросов, который ClickHouse использовал до версии 24.3, больше не поддерживается. Перечисленные ниже несовместимости описывают, чем отличался прежний анализ, чтобы можно было обновить написанный под него запрос; чтобы увидеть его поведение, выполните запрос на версии ClickHouse старше 26.9.
Известные несовместимости
Несмотря на исправление большого количества ошибок и внедрение новых оптимизаций, это также приводит к некоторым несовместимым изменениям в поведении ClickHouse. Ознакомьтесь со следующими изменениями, чтобы понять, как переписать ваши запросы для анализатора.Некорректные запросы больше не оптимизируются
Прежняя инфраструктура планирования запросов применяла оптимизации на уровне AST до этапа проверки запроса. Оптимизации могли переписать исходный запрос так, чтобы он стал корректным и исполнимым. В анализаторе проверка запроса выполняется до этапа оптимизации. Это означает, что некорректные запросы, которые раньше можно было выполнить, теперь не поддерживаются. В таких случаях запрос необходимо исправить вручную.Пример 1
Следующий запрос использует столбецnumber в списке проекций, хотя после агрегации доступно только toString(number).
В старом анализаторе GROUP BY toString(number) оптимизировалось до GROUP BY number,, что делало запрос корректным.
Пример 2
Та же проблема возникает и в этом запросе. Столбецnumber используется после агрегации с другим ключом.
Прежний анализатор запросов исправлял этот запрос, перемещая фильтр number > 5 из условия HAVING в условие WHERE.
WHERE, чтобы привести его в соответствие со стандартным синтаксисом SQL:
HAVING в WHERE для неагрегатных AND-конъюнктов. Чтобы включить это поведение, задайте analyzer_compatibility_allow_non_aggregate_in_having = 1. Этот параметр доступен начиная с ClickHouse 26.7. Параметр игнорируется для WITH CUBE, WITH ROLLUP, WITH TOTALS и GROUPING SETS. Конъюнкты, содержащие агрегатные функции, grouping или недетерминированные функции, остаются в HAVING; если какой-либо конъюнкт содержит оконную функцию или функцию с сохранением состояния (например, rowNumberInBlock), преобразование отключается для всего HAVING, что соответствует прежнему поведению.
CREATE VIEW с некорректным запросом
Анализатор всегда выполняет проверку типов.
Ранее можно было создать VIEW с некорректным запросом SELECT.
В этом случае ошибка возникала при первом SELECT или INSERT (в случае MATERIALIZED VIEW).
Теперь создать VIEW таким способом нельзя.
Пример
Известные несовместимости предложения JOIN
JOIN с использованием столбца из проекции
По умолчанию псевдоним из списка SELECT нельзя использовать в качестве ключа JOIN USING.
Новая настройка analyzer_compatibility_join_using_top_level_identifier, если она включена, меняет поведение JOIN USING: при разрешении идентификаторов предпочтение отдается выражениям из списка проекций запроса SELECT, а не столбцам из левой таблицы напрямую.
Например:
analyzer_compatibility_join_using_top_level_identifier установлено в true, условие JOIN интерпретируется как t1.a + 1 = t2.b, что соответствует поведению в более ранних версиях.
Результат будет 2, 'two'.
Если эта настройка имеет значение false, по умолчанию используется условие JOIN t1.b = t2.b, и запрос вернёт 2, 'one'.
Если b отсутствует в t1, запрос завершится ошибкой.
Изменения в поведении JOIN USING со столбцами ALIAS/MATERIALIZED
В анализаторе использование * в запросе JOIN USING со столбцами ALIAS или MATERIALIZED по умолчанию включает эти столбцы в результирующий набор.
Например:
payload вместе с id из обеих таблиц.
В отличие от него, предыдущий анализатор включал эти столбцы ALIAS только при включении определённых настроек (asterisk_include_alias_columns или asterisk_include_materialized_columns),
при этом столбцы могли отображаться в другом порядке.
Чтобы результаты были предсказуемыми и согласованными, особенно при миграции старых запросов на анализатор, рекомендуется явно указывать столбцы в секции SELECT, а не использовать *.
Обработка модификаторов типов столбцов в предложении USING
В анализаторе правила определения общего супертипа для столбцов, указанных в предложении USING, были унифицированы, чтобы результаты стали более предсказуемыми,
особенно при работе с такими модификаторами типов, как LowCardinality и Nullable.
LowCardinality(T)иT: если столбец типаLowCardinality(T)участвует в JOIN со столбцом типаT, результирующим общим супертипом будетT, то есть модификаторLowCardinalityфактически отбрасывается.Nullable(T)иT: если столбец типаNullable(T)участвует в JOIN со столбцом типаT, результирующим общим супертипом будетNullable(T), что гарантирует сохранение свойства nullable.
id определяется как String, а модификатор LowCardinality из t1 отбрасывается.
Изменения в именах столбцов проекции
При вычислении имён проекции псевдонимы не подставляются.24.3 второй столбец получал имя по подставленному псевдониму:
Несовместимые типы аргументов функции
В анализатор вывод типов происходит на этапе начального анализа запроса. Это означает, что проверка типов выполняется до укороченного вычисления, поэтому аргументы функцииif всегда должны иметь общий супертип.
Например, следующий запрос завершается ошибкой There is no supertype for types Array(UInt8), String because some of them are Array and some of them are not:
Неоднородные кластеры
Анализатор существенно меняет протокол обмена данными между серверами в кластере. Поэтому невозможно выполнять распределённые запросы на серверах, которые расходятся в том, используется ли анализатор, — а для кластера серверов версий старше26.9 это означает серверы с разными значениями настройки enable_analyzer.
В сервере версии 26.10 и новее другого механизма анализа запросов не осталось, поэтому он игнорирует значение, присылаемое более старым initiator, и в любом случае анализирует запрос с помощью анализатора. Два варианта анализа именуют результирующие столбцы по-разному, а initiator сопоставляет блок, возвращаемый сегментом, по имени столбца, поэтому такой запрос может завершиться на initiator ошибкой NOT_FOUND_COLUMN_IN_BLOCK — например, когда в нём выбирается функция, записанная в неканоническом регистре (hostname()), которую анализатор приводит к каноническому имени (hostName()). Поэтому в кластере, всё ещё работающем со старым механизмом анализа запросов, необходимо установить enable_analyzer = 1 на каждом сервере до того, как любой из них будет обновлён до 26.10.
Неподдерживаемые возможности
Ниже приведен список возможностей, которые анализатор пока не поддерживает:- Индекс Annoy.
- Индекс Hypothesis. Работа над ним ведется здесь.
Миграция в Cloud
Мы включаем анализатор на всех экземплярах, где он сейчас отключен, чтобы обеспечить новые функциональные возможности и оптимизации производительности. Это изменение ужесточает правила области видимости в SQL, поэтому клиентам потребуется вручную обновить запросы, которые им не соответствуют.Процесс миграции
- Определите запрос, отфильтровав записи в
system.query_logпоnormalized_query_hash:
- Выполните запрос с анализатором, добавив настройку совместимости (compatibility setting), которая восстанавливает разрешение идентификаторов прежнего анализа, если запрос на это опирается.
- Доработайте запрос и проверьте, что его результаты совпадают с выводом, который запрос давал до миграции.
Неизвестный идентификатор выражения
Ошибка:Unknown expression identifier ... in scope ... (UNKNOWN_IDENTIFIER). Код исключения: 47
Причина: запросы, зависящие от нестандартного устаревшего поведения, допускающего неоднозначности, — например, обращения к вычисляемым псевдонимам в фильтрах, неоднозначных проекций подзапросов или «динамической» области видимости CTE, — теперь корректно определяются как недопустимые и сразу отклоняются.
Решение: обновите SQL-шаблоны следующим образом:
- Логика фильтрации: перенесите условие из WHERE в HAVING, если фильтрация идёт по результатам, или продублируйте выражение в WHERE, если фильтрация идёт по исходным данным.
- Область видимости подзапроса: явно выберите все столбцы, необходимые внешнему запросу.
- Ключи JOIN: используйте ON с полными выражениями вместо USING, если ключ — это псевдоним.
- Во внешних запросах обращайтесь к псевдониму самого подзапроса/CTE, а не к таблицам внутри него.
Неагрегированные столбцы в GROUP BY
Ошибка:Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE). Код исключения: 215
Причина: Старый анализатор позволял выбирать столбцы, которых нет в GROUP BY (часто подставляя произвольное значение). Анализатор следует стандарту SQL: каждый выбранный столбец должен быть либо агрегатом, либо ключом группировки.
Решение: Оберните столбец в any(), argMax() или добавьте его в GROUP BY.
Неагрегированные столбцы в HAVING
Ошибка:Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE). Код исключения: 215
Причина: Старый анализатор без предупреждения переносил неагрегирующие AND-конъюнкты из HAVING в WHERE, рассматривая их как фильтры предварительной агрегации. Анализатор соответствует standard SQL: HAVING может ссылаться только на ключи агрегации и агрегатные функции.
Решение: Вручную перенесите предикат из HAVING в WHERE или включите analyzer_compatibility_allow_non_aggregate_in_having = 1 (доступно начиная с ClickHouse 26.7), чтобы вернуть прежнее преобразование как вспомогательное средство при миграции. Настройка совместимости не применяется к WITH CUBE, WITH ROLLUP, WITH TOTALS и GROUPING SETS. Конъюнкты, содержащие агрегатные, grouping или недетерминированные функции, остаются в HAVING; если хотя бы один конъюнкт содержит оконную функцию или функцию с сохранением состояния (например, rowNumberInBlock), преобразование отключается для всего HAVING, как и в прежнем поведении.
Повторяющиеся имена CTE
Ошибка:CTE with name ... already exists (MULTIPLE_EXPRESSIONS_FOR_ALIAS). Код исключения: 179
Причина: старый анализатор позволял определять несколько общих табличных выражений (WITH …) с одинаковым именем, при этом более позднее определение перекрывало предыдущее. Новый анализатор по умолчанию не допускает такой неоднозначности.
Решение: переименуйте повторяющиеся CTE так, чтобы их имена стали уникальными. В качестве временной меры на период миграции можно включить analyzer_compatibility_allow_cte_redefinition = 1 (доступно начиная с ClickHouse 26.10) и тем самым восстановить прежнее поведение: ссылка привязывается к последнему определению имени, которое не разрешается в данный момент. Благодаря этому переопределение может обращаться к предыдущему определению, а тело запроса — к последнему.
Ограничения: CTE, объявленное как MATERIALIZED, и CTE в предложении WITH RECURSIVE нельзя переопределить даже при включенной настройке. Кроме того, одна конструкция запроса обрабатывается иначе, чем в старом анализаторе: CTE, объявленное между двумя определениями одного и того же имени, тоже привязывается к последнему определению, тогда как старый анализатор привязывал его к определению, видимому в месте его объявления.
Неоднозначные идентификаторы столбцов
Ошибка:JOIN [JOIN TYPE] ambiguous identifier ... (AMBIGUOUS_IDENTIFIER) Код исключения: 207
Причина: В запросе используется имя столбца, которое присутствует в нескольких таблицах в JOIN, без указания исходной таблицы. Старый анализатор часто определял нужный столбец на основе внутренней логики, тогда как анализатор требует явного указания имени.
Решение: Полностью указывайте столбец в виде table_alias.column_name.
Недопустимое использование FINAL
Ошибка:Table expression modifiers FINAL are not supported for subquery... или Storage ... doesn't support FINAL (UNSUPPORTED_METHOD). Коды исключений: 1, 181
Причина: FINAL — это модификатор хранения таблицы (в частности, для [Shared]ReplacingMergeTree). Анализатор отклоняет FINAL, если он применяется к:
- Подзапросам или производным таблицам (например, FROM (SELECT …) FINAL).
- Движкам таблиц, которые его не поддерживают (например, SharedMergeTree).
Чувствительность функции countDistinct() к регистру
Ошибка: Function with name countdistinct does not exist (UNKNOWN_FUNCTION). Код исключения: 46
Причина: имена функций чувствительны к регистру, либо в анализаторе для них используется строгое сопоставление. countdistinct (полностью в нижнем регистре) больше не распознаётся автоматически.
Решение: используйте стандартную countDistinct (camelCase) или специфичную для ClickHouse функцию uniq.