24.3 do ClickHouse, o analisador de consultas foi ativado por padrão.
Você pode ler mais detalhes sobre como ele funciona aqui.
A partir da versão 26.9, o analisador é obrigatório: a configuração enable_analyzer está obsoleta, qualquer tentativa de defini-la como 0 é rejeitada e a análise de consultas que o ClickHouse usava antes da versão 24.3 não é mais compatível. As incompatibilidades listadas abaixo descrevem como essa análise mais antiga se diferenciava, de modo que uma consulta escrita para ela possa ser atualizada; para observar esse comportamento, execute a consulta em uma versão do ClickHouse anterior à 26.9.
Incompatibilidades conhecidas
Apesar de corrigir um grande número de bugs e introduzir novas otimizações, isso também traz algumas mudanças incompatíveis no comportamento do ClickHouse. Leia as alterações a seguir para entender como reescrever suas consultas para o analisador.Consultas inválidas não são mais otimizadas
A infraestrutura anterior de planejamento de consultas aplicava otimizações no nível da AST antes da etapa de validação da consulta. Essas otimizações podiam reescrever a consulta original para torná-la válida e executável. No analisador, a validação da consulta ocorre antes da etapa de otimização. Isso significa que consultas inválidas que antes podiam ser executadas agora não são mais suportadas. Nesses casos, a consulta precisa ser corrigida manualmente.Exemplo 1
A consulta a seguir usa a colunanumber na lista de projeção quando apenas toString(number) fica disponível após a agregação.
No analisador antigo, GROUP BY toString(number) era otimizado para GROUP BY number,, o que tornava a consulta válida.
Exemplo 2
O mesmo problema ocorre nesta consulta. A colunanumber é usada após a agregação junto com outra chave.
O analisador de consultas anterior corrigiu essa consulta movendo o filtro number > 5 da cláusula HAVING para a cláusula WHERE.
WHERE, para seguir a sintaxe SQL padrão:
HAVING para WHERE para conjunções AND não agregadas. Ative analyzer_compatibility_allow_non_aggregate_in_having = 1 para habilitar esse comportamento. A configuração está disponível desde o ClickHouse 26.7. A configuração é ignorada para WITH CUBE, WITH ROLLUP, WITH TOTALS e GROUPING SETS. Conjunções que contêm funções de agregação, grouping ou funções não determinísticas permanecem em HAVING; se alguma conjunção contiver uma função de janela ou uma função com estado (por exemplo, rowNumberInBlock), a reescrita será desabilitada para todo o HAVING, em conformidade com o comportamento legacy.
CREATE VIEW com uma consulta inválida
O analisador sempre realiza a verificação de tipos.
Anteriormente, era possível criar uma VIEW com uma consulta SELECT inválida.
A falha só ocorria no primeiro SELECT ou INSERT (no caso de MATERIALIZED VIEW).
Não é mais possível criar uma VIEW dessa maneira.
Exemplo
Incompatibilidades conhecidas da cláusula JOIN
JOIN usando uma coluna de uma projeção
Por padrão, um alias da lista SELECT não pode ser usado como chave em JOIN USING.
Uma nova configuração, analyzer_compatibility_join_using_top_level_identifier, quando habilitada, altera o comportamento de JOIN USING para priorizar a resolução de identificadores com base em expressões da lista de projeção da consulta SELECT, em vez de usar diretamente as colunas da tabela da esquerda.
Por exemplo:
analyzer_compatibility_join_using_top_level_identifier definido como true, a condição de join é interpretada como t1.a + 1 = t2.b, em conformidade com o comportamento das versões anteriores.
O resultado será 2, 'two'.
Quando a configuração estiver definida como false, a condição de join será, por padrão, t1.b = t2.b, e a consulta retornará 2, 'one'.
Se b não estiver presente em t1, a consulta falhará com um erro.
Mudanças no comportamento com JOIN USING e colunas ALIAS/MATERIALIZED
No analisador, o uso de * em uma consulta com JOIN USING que envolve colunas ALIAS ou MATERIALIZED incluirá essas colunas no conjunto de resultados por padrão.
Por exemplo:
payload juntamente com id de ambas as tabelas.
Em contrapartida, o analisador anterior só incluiria essas colunas ALIAS se configurações específicas (asterisk_include_alias_columns ou asterisk_include_materialized_columns) estivessem habilitadas,
e as colunas poderiam aparecer em uma ordem diferente.
Para garantir resultados consistentes e previsíveis, especialmente ao migrar consultas antigas para o analisador, é recomendável especificar explicitamente as colunas na cláusula SELECT, em vez de usar *.
Tratamento de modificadores de tipo para colunas na cláusula USING
No analisador, as regras para determinar o supertipo comum de colunas especificadas na cláusula USING foram padronizadas para produzir resultados mais previsíveis,
especialmente ao lidar com modificadores de tipo como LowCardinality e Nullable.
LowCardinality(T)eT: Quando uma coluna do tipoLowCardinality(T)é combinada com uma coluna do tipoTem umjoin, o supertipo comum resultante seráT, descartando efetivamente o modificadorLowCardinality.Nullable(T)eT: Quando uma coluna do tipoNullable(T)é combinada com uma coluna do tipoTem umjoin, o supertipo comum resultante seráNullable(T), garantindo que a anulabilidade seja preservada.
id é definido como String, descartando o modificador LowCardinality de t1.
Alterações nos nomes das colunas da projeção
Ao calcular os nomes da projeção, os aliases não são substituídos.24.3, a segunda coluna recebia o nome do alias substituído:
Tipos incompatíveis de argumentos de função
No analisador, a inferência de tipos ocorre durante a análise inicial da consulta. Essa mudança significa que as verificações de tipo são feitas antes da avaliação de curto-circuito; portanto, os argumentos da funçãoif devem sempre ter um supertipo comum.
Por exemplo, a consulta a seguir falha com There is no supertype for types Array(UInt8), String because some of them are Array and some of them are not:
Clusters heterogêneos
O analisador altera significativamente o protocolo de comunicação entre os servidores do cluster. Portanto, é impossível executar consultas distribuídas em servidores que não concordam quanto ao uso do analisador — o que, para um cluster de servidores anteriores à versão26.9, significa servidores com valores diferentes para a configuração enable_analyzer.
Um servidor na versão 26.10 ou mais recente não conta com nenhuma outra análise de consulta, de modo que ignora o valor enviado por um initiator mais antigo e analisa a consulta com o analisador de qualquer forma. As duas análises não nomeiam as colunas do resultado da mesma maneira, e o initiator faz a correspondência do bloco retornado por um shard pelo nome da coluna, de modo que tal consulta pode falhar no initiator com NOT_FOUND_COLUMN_IN_BLOCK — por exemplo, quando ela seleciona uma função escrita em caixa não canônica (hostname()), que o analisador resolve para seu nome canônico (hostName()). Portanto, um cluster que ainda esteja operando com a análise de consulta antiga deve definir enable_analyzer = 1 em todos os servidores antes que qualquer um deles receba upgrade para a versão 26.10.
Recursos sem suporte
A lista de recursos aos quais o analisador atualmente não dá suporte é apresentada abaixo:- Índice Annoy.
- Índice Hypothesis. Trabalho em andamento aqui.
Migração para Cloud
Estamos habilitando o analisador em todas as instâncias nas quais ele ainda está desativado, para oferecer suporte a novas otimizações funcionais e de desempenho. Essa mudança impõe regras mais rigorosas de escopo em SQL, exigindo que os clientes atualizem manualmente as consultas que não estiverem em conformidade.Fluxo de migração
- Identifique a consulta filtrando
system.query_logpelonormalized_query_hash:
- Execute a consulta com o analisador, adicionando a configuração de compatibilidade que restaura a resolution de identificadores da análise antiga nos casos em que a consulta depende dela.
- Refatore e verifique os resultados da consulta para garantir que correspondam à saída que a consulta produzia antes da migração.
Identificador de expressão desconhecido
Erro:Unknown expression identifier ... in scope ... (UNKNOWN_IDENTIFIER). Código da exceção: 47
Causa: Consultas que dependem de comportamentos legados não padronizados e permissivos, como referenciar aliases calculados em filtros, projeções ambíguas de subconsultas ou escopo “dinâmico” de CTEs, agora são corretamente identificadas como inválidas e rejeitadas de imediato.
Solução: Atualize seus padrões SQL da seguinte forma:
- Lógica de filtro: Mova a lógica de WHERE para HAVING se estiver filtrando resultados, ou duplique a expressão em WHERE se estiver filtrando dados de origem.
- Escopo da subconsulta: Selecione explicitamente todas as colunas necessárias para a consulta externa.
- Chaves de JOIN: Use ON com expressões completas em vez de USING se a chave for um alias.
- Em consultas externas, use o alias da própria subconsulta/CTE, não o das tabelas dentro dela.
Colunas não agregadas em GROUP BY
Erro:Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE). Código da exceção: 215
Causa: O analisador antigo permitia selecionar colunas que não estavam presentes na cláusula GROUP BY (muitas vezes escolhendo um valor arbitrário). O analisador segue o padrão SQL: toda coluna selecionada deve ser uma agregação ou uma chave de agrupamento.
Solução: Envolva a coluna em any(), argMax() ou adicione-a ao GROUP BY.
Colunas não agregadas em HAVING
Erro:Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE). Código da exceção: 215
Causa: O analisador antigo movia silenciosamente os conjuntos não agregados unidos por AND de HAVING para WHERE, tratando-os como filtros de pré-agregação. O analisador segue o SQL padrão: HAVING só pode referenciar chaves de agregação e funções de agregação.
Solução: Mova manualmente o predicado de HAVING para WHERE ou habilite analyzer_compatibility_allow_non_aggregate_in_having = 1 (disponível desde o ClickHouse 26.7) para restaurar a reescrita legada como auxílio à migração. A configuração de compatibilidade é ignorada para WITH CUBE, WITH ROLLUP, WITH TOTALS e GROUPING SETS. Os conjuntos que contêm funções de agregação, grouping ou funções não determinísticas permanecem em HAVING; se algum conjunto contiver uma função de janela ou uma função com estado (por exemplo, rowNumberInBlock), a reescrita será desabilitada para todo o HAVING, em linha com o comportamento legado.
Nomes de CTE duplicados
Erro:CTE with name ... already exists (MULTIPLE_EXPRESSIONS_FOR_ALIAS). Código de exceção: 179
Causa: O analisador antigo permitia definir várias expressões de tabela comuns (WITH …) com o mesmo nome, sendo que uma definição posterior sobrepunha a anterior. O analisador rejeita essa ambiguidade por padrão.
Solução: Renomeie as CTEs duplicadas para que tenham nomes únicos. Para facilitar a migração, habilite analyzer_compatibility_allow_cte_redefinition = 1 (disponível a partir do ClickHouse 26.10) para restaurar o comportamento legado: uma referência é vinculada à definição mais recente do nome que não esteja sendo resolvida naquele momento. Assim, uma redefinição pode ler a definição anterior, e o corpo da consulta lê a última.
Limitações: uma CTE declarada como MATERIALIZED e uma CTE em uma cláusula WITH RECURSIVE não podem ser redefinidas, mesmo com a configuração habilitada. Há um formato de consulta em que o comportamento difere do analisador antigo: uma CTE declarada entre duas definições de um mesmo nome também é vinculada à última definição, enquanto o analisador antigo a vinculava à definição visível no ponto em que ela foi declarada.
Identificadores de coluna ambíguos
Erro:JOIN [JOIN TYPE] ambiguous identifier ... (AMBIGUOUS_IDENTIFIER) Código da exceção: 207
Causa: A consulta faz referência a um nome de coluna presente em várias tabelas em um JOIN sem especificar a tabela de origem. O analisador antigo frequentemente deduzia a coluna com base na lógica interna; o analisador exige um nome explícito.
Solução: Qualifique totalmente a coluna com table_alias.column_name.
Uso inválido de FINAL
Erro:Table expression modifiers FINAL are not supported for subquery... ou Storage ... doesn't support FINAL (UNSUPPORTED_METHOD). Códigos de exceção: 1, 181
Causa: FINAL é um modificador de armazenamento de tabela (especificamente [Shared]ReplacingMergeTree). O analisador rejeita FINAL quando é aplicado a:
- Subconsultas ou tabelas derivadas (por exemplo, FROM (SELECT …) FINAL).
- Motores de tabela que não oferecem suporte a ele (por exemplo, SharedMergeTree).
Sensibilidade a maiúsculas e minúsculas na função countDistinct()
Erro: Function with name countdistinct does not exist (UNKNOWN_FUNCTION). Código da exceção: 46
Causa: Os nomes de funções diferenciam maiúsculas de minúsculas ou são mapeados estritamente pelo analisador. countdistinct (tudo em minúsculas) não é mais resolvida automaticamente.
Solução: Use a countDistinct padrão (camelCase) ou a uniq, específica do ClickHouse.