Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Analizador

En la versión 24.3 de ClickHouse, el analizador de consultas estaba habilitado de forma predeterminada. Puede obtener más información sobre su funcionamiento aquí.

Incompatibilidades conocidas

A pesar de corregir una gran cantidad de errores e introducir nuevas optimizaciones, también se introducen algunos cambios incompatibles en el comportamiento de ClickHouse. Lea los siguientes cambios para determinar cómo reescribir sus consultas para el analizador.

Las consultas no válidas ya no se optimizan

La infraestructura anterior de planificación de consultas aplicaba optimizaciones a nivel de AST antes del paso de validación de la consulta. Las optimizaciones podían reescribir la consulta inicial para que fuera válida y ejecutable.

En el analizador, la validación de la consulta se realiza antes del paso de optimización. Esto significa que las consultas no válidas que antes podían ejecutarse ahora ya no se admiten. En esos casos, la consulta debe corregirse manualmente.

Ejemplo 1

La siguiente consulta usa la columna number en la lista de proyección cuando, tras la agregación, solo está disponible toString(number). En el analizador anterior, GROUP BY toString(number) se optimizaba a GROUP BY number,, lo que hacía que la consulta fuera válida.

SELECT number
FROM numbers(1)
GROUP BY toString(number)

Ejemplo 2

El mismo problema se produce en esta consulta. La columna number se usa después de la agregación con otra clave. El analizador de consultas anterior corrigió esta consulta al mover el filtro number > 5 de la cláusula HAVING a la cláusula WHERE.

SELECT
    number % 2 AS n,
    sum(number)
FROM numbers(10)
GROUP BY n
HAVING number > 5

Para corregir la consulta, debes mover a la sección WHERE todas las condiciones que se apliquen a columnas no agregadas, para ajustarte a la sintaxis SQL estándar:

SELECT
    number % 2 AS n,
    sum(number)
FROM numbers(10)
WHERE number > 5
GROUP BY n

Como ayuda para la migración, el analizador puede replicar la antigua reescritura de HAVING a WHERE para conjunciones AND no agregadas. Habilita analyzer_compatibility_allow_non_aggregate_in_having = 1 para activar este comportamiento. Esta configuración está disponible desde ClickHouse 26.7. La configuración se ignora para WITH CUBE, WITH ROLLUP, WITH TOTALS y GROUPING SETS. Las conjunciones que contienen funciones de agregación, grouping o no deterministas permanecen en HAVING; si alguna conjunción contiene una función de ventana o una función con estado (por ejemplo, rowNumberInBlock), la reescritura se desactiva para toda la cláusula HAVING, en consonancia con el comportamiento heredado.

CREATE VIEW con una consulta no válida

El analizador siempre realiza la verificación de tipos. Anteriormente, era posible crear una VIEW con una consulta SELECT no válida. El error aparecía durante el primer SELECT o INSERT (en el caso de MATERIALIZED VIEW).

Ya no es posible crear una VIEW de esta forma.

Ejemplo

CREATE TABLE source (data String)
ENGINE=MergeTree
ORDER BY tuple();

CREATE VIEW some_view
AS SELECT JSONExtract(data, 'test', 'DateTime64(3)')
FROM source;

Incompatibilidades conocidas de la cláusula JOIN

JOIN usando una columna de una proyección

De forma predeterminada, no se puede usar un alias de la lista SELECT como clave de JOIN USING.

Una nueva configuración, analyzer_compatibility_join_using_top_level_identifier, cuando está habilitada, cambia el comportamiento de JOIN USING para que dé prioridad a la resolución de identificadores a partir de las expresiones de la lista de proyección de la consulta SELECT, en lugar de usar directamente las columnas de la tabla de la izquierda.

Por ejemplo:

SELECT a + 1 AS b, t2.s
FROM VALUES('a UInt64, b UInt64', (1, 1)) AS t1
JOIN VALUES('b UInt64, s String', (1, 'one'), (2, 'two')) t2
USING (b);

Con analyzer_compatibility_join_using_top_level_identifier establecido en true, la condición de join se interpreta como t1.a + 1 = t2.b, en consonancia con el comportamiento de las versiones anteriores. El resultado será 2, 'two'. Cuando la configuración está en false, la condición de join pasa a ser t1.b = t2.b de forma predeterminada, y la consulta devolverá 2, 'one'. Si b no está presente en t1, la consulta fallará con un error.

Cambios de comportamiento con JOIN USING y columnas ALIAS/MATERIALIZED

En el analizador, usar * en una consulta JOIN USING que involucre columnas ALIAS o MATERIALIZED hará que esas columnas se incluyan en el conjunto de resultados de forma predeterminada.

Por ejemplo:

CREATE TABLE t1 (id UInt64, payload ALIAS sipHash64(id)) ENGINE = MergeTree ORDER BY id;
INSERT INTO t1 VALUES (1), (2);

CREATE TABLE t2 (id UInt64, payload ALIAS sipHash64(id)) ENGINE = MergeTree ORDER BY id;
INSERT INTO t2 VALUES (2), (3);

SELECT * FROM t1
FULL JOIN t2 USING (payload);

En el analizador, el resultado de esta consulta incluirá la columna payload junto con id de ambas tablas. En cambio, el analizador anterior solo incluía estas columnas ALIAS si se habían habilitado opciones específicas (asterisk_include_alias_columns o asterisk_include_materialized_columns), y las columnas podían aparecer en un orden diferente.

Para garantizar resultados coherentes y previsibles, especialmente al migrar consultas antiguas al analizador, es aconsejable especificar las columnas explícitamente en la cláusula SELECT en lugar de usar *.

Manejo de los modificadores de tipo de las columnas en la cláusula USING

En el analizador, se han estandarizado las reglas para determinar el supertipo común de las columnas especificadas en la cláusula USING, con el fin de producir resultados más predecibles, especialmente al trabajar con modificadores de tipo como LowCardinality y Nullable.

  • LowCardinality(T) y T: Cuando una columna de tipo LowCardinality(T) se combina con una columna de tipo T, el supertipo común resultante será T, lo que en la práctica descarta el modificador LowCardinality.
  • Nullable(T) y T: Cuando una columna de tipo Nullable(T) se combina con una columna de tipo T, el supertipo común resultante será Nullable(T), lo que garantiza que se conserve la propiedad Nullable.

Por ejemplo:

SELECT id, toTypeName(id)
FROM VALUES('id LowCardinality(String)', ('a')) AS t1
FULL OUTER JOIN VALUES('id String', ('b')) AS t2
USING (id);

En esta consulta, el supertipo común de id se establece como String, descartando el modificador LowCardinality de t1.

Cambios en los nombres de las columnas de la proyección

Durante el cálculo de los nombres de la proyección, no se sustituyen los alias.

SELECT
    1 + 1 AS x,
    x + 1
SETTINGS enable_analyzer = 0
FORMAT PrettyCompact

Tipos incompatibles en los argumentos de función

En el analizador, la inferencia de tipos se produce durante el análisis inicial de la consulta. Este cambio significa que las comprobaciones de tipos se realizan antes de la evaluación de cortocircuito; por lo tanto, los argumentos de la función if siempre deben tener un supertipo común.

Por ejemplo, la siguiente consulta falla con There is no supertype for types Array(UInt8), String because some of them are Array and some of them are not:

SELECT toTypeName(if(0, [2, 3, 4], 'String'))

Clústeres heterogéneos

El analizador cambia de forma significativa el protocolo de comunicación entre los servidores del clúster. Por lo tanto, es imposible ejecutar consultas distribuidas en servidores con valores distintos de la configuración enable_analyzer.

Las mutaciones se interpretan con el analizador anterior

Las mutaciones siguen usando el analizador anterior. Esto significa que algunas funciones nuevas de ClickHouse SQL no se pueden usar en las mutaciones. Por ejemplo, la cláusula QUALIFY. Se puede consultar el estado aquí.

Funcionalidades no compatibles

A continuación se muestra la lista de funcionalidades que el analizador no admite actualmente:

  • Índice Annoy.
  • Índice Hypothesis. Trabajo en curso aquí.
  • window view no es compatible. No hay planes de admitirla en el futuro.

Migración a Cloud

Estamos habilitando el analizador en todas las instancias donde actualmente está deshabilitado para permitir nuevas optimizaciones funcionales y de rendimiento. Este cambio impone reglas de ámbito de SQL más estrictas, por lo que los clientes deberán actualizar manualmente las consultas que no las cumplan.

Flujo de migración

  1. Identifique la consulta filtrando system.query_log con normalized_query_hash:
SELECT query 
FROM clusterAllReplicas(default, system.query_log)
WHERE normalized_query_hash='{hash}' 
LIMIT 1 
SETTINGS skip_unavailable_shards=1
  1. Ejecute la consulta con el analizador habilitado agregando esta configuración.
SETTINGS
    enable_analyzer=1,
    analyzer_compatibility_join_using_top_level_identifier=1
  1. Refactoriza y verifica los resultados de la consulta para asegurarte de que coincidan con la salida generada cuando el analizador está desactivado.

Consulta las incompatibilidades más frecuentes detectadas durante las pruebas internas.

Identificador de expresión desconocido

Error: Unknown expression identifier ... in scope ... (UNKNOWN_IDENTIFIER). Código de excepción: 47

Causa: Las consultas que dependen de comportamientos heredados permisivos y no estándar, como hacer referencia a alias calculados en filtros, proyecciones ambiguas de subconsultas o un alcance "dinámico" de CTE, ahora se identifican correctamente como no válidas y se rechazan de inmediato.

Solución: Actualice sus patrones SQL de la siguiente manera:

  • Lógica de filtro: Mueva la lógica de WHERE a HAVING si filtra por resultados, o duplique la expresión en WHERE si filtra por datos de origen.
  • Alcance de la subconsulta: Seleccione explícitamente todas las columnas necesarias para la consulta externa.
  • Claves de JOIN: Use ON con expresiones completas en lugar de USING si la clave es un alias.
  • En las consultas externas, haga referencia al alias de la propia subconsulta/CTE, no a las tablas que contiene.

Columnas no agregadas en GROUP BY

Error: Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE). Código de excepción: 215

Causa: El analizador anterior permitía seleccionar columnas que no estaban presentes en la cláusula GROUP BY (a menudo tomando un valor arbitrario). El analizador sigue el estándar SQL: cada columna seleccionada debe ser un agregado o una clave de agrupación.

Solución: Envuelva la columna en any(), argMax() o añádala a GROUP BY.

/* CONSULTA ORIGINAL */
-- device_id es ambiguo
SELECT user_id, device_id FROM table GROUP BY user_id

/* CONSULTA CORREGIDA */
SELECT user_id, any(device_id) FROM table GROUP BY user_id
-- O
SELECT user_id, device_id FROM table GROUP BY user_id, device_id

Columnas no agregadas en HAVING

Error: Column ... is not under aggregate function and not in GROUP BY keys (NOT_AN_AGGREGATE). código de excepción: 215

Causa: El analizador anterior movía silenciosamente a WHERE las conjunciones con AND de HAVING que no eran de agregación, tratándolas como filtros de preagregación. El analizador se ajusta al SQL estándar: HAVING solo puede hacer referencia a claves de agregación y funciones de agregación.

Solución: Mueva manualmente el predicado de HAVING a WHERE, o habilite analyzer_compatibility_allow_non_aggregate_in_having = 1 (disponible desde ClickHouse 26.7) para restaurar la reescritura heredada como ayuda para la migración. La configuración compatibility se ignora para WITH CUBE, WITH ROLLUP, WITH TOTALS y GROUPING SETS. Las conjunciones que contienen funciones de agregación, grouping o funciones no deterministas permanecen en HAVING; si alguna conjunción contiene una función de ventana o una función con estado (por ejemplo, rowNumberInBlock), la reescritura se desactiva para todo el HAVING, en consonancia con el comportamiento heredado.

/* ORIGINAL QUERY */
SELECT category, sum(value) FROM t GROUP BY category HAVING service = 'svc1';

/* FIXED QUERY */
SELECT category, sum(value) FROM t WHERE service = 'svc1' GROUP BY category;

Nombres de CTE duplicados

Error: CTE with name ... already exists (MULTIPLE_EXPRESSIONS_FOR_ALIAS). Código de excepción: 179

Causa: El analizador anterior permitía definir varias Common Table Expressions (WITH …) con el mismo nombre, de modo que una ocultaba a la anterior. El analizador prohíbe esta ambigüedad.

Solución: Renombre los CTE duplicados para que tengan nombres únicos.

/* CONSULTA ORIGINAL */
WITH 
  data AS (SELECT 1 AS id), 
  data AS (SELECT 2 AS id) -- Redefinida
SELECT * FROM data;

/* CONSULTA CORREGIDA */
WITH 
  raw_data AS (SELECT 1 AS id), 
  processed_data AS (SELECT 2 AS id)
SELECT * FROM processed_data;

Identificadores de columna ambiguos

Error: JOIN [JOIN TYPE] ambiguous identifier ... (AMBIGUOUS_IDENTIFIER) Código de excepción: 207

Causa: La consulta hace referencia a un nombre de columna presente en varias tablas dentro de un JOIN sin especificar la tabla de origen. El analizador antiguo a menudo deducía la columna según su lógica interna; el analizador requiere un nombre explícito.

Solución: Especifique el nombre completo de la columna con table_alias.column_name.

/* CONSULTA ORIGINAL */
SELECT table1.ID AS ID FROM table1, table2 WHERE ID...

/* CONSULTA CORREGIDA */
SELECT table1.ID AS ID_RENAMED FROM table1, table2 WHERE ID_RENAMED...

Uso no válido de FINAL

Error: Table expression modifiers FINAL are not supported for subquery... o Storage ... doesn't support FINAL (UNSUPPORTED_METHOD). Códigos de excepción: 1, 181

Causa: FINAL es un modificador del almacenamiento de tablas (en concreto, [Shared]ReplacingMergeTree). El analizador rechaza FINAL cuando se aplica a:

  • Subconsultas o tablas derivadas (p. ej., FROM (SELECT …) FINAL).
  • Motores de tabla que no lo admiten (p. ej., SharedMergeTree).

Solución: Aplique FINAL solo a la tabla de origen dentro de la subconsulta, o elimínelo si el motor no lo admite.

/* CONSULTA ORIGINAL */
SELECT * FROM (SELECT * FROM my_table) AS subquery FINAL ...

/* CONSULTA CORREGIDA */
SELECT * FROM (SELECT * FROM my_table FINAL) AS subquery ...

Insensibilidad a mayúsculas y minúsculas en la función countDistinct()

Error: Function with name countdistinct does not exist (UNKNOWN_FUNCTION). Código de excepción: 46

Causa: Los nombres de las funciones son sensibles a mayúsculas y minúsculas o están asociados de forma estricta en el analizador. countdistinct (todo en minúsculas) ya no se resuelve automáticamente.

Solución: Use countDistinct estándar (camelCase) o uniq, específico de ClickHouse.

Navigation