IN, NOT IN, GLOBAL IN y GLOBAL NOT IN se tratan por separado, dado que su funcionalidad es bastante extensa.
El lado izquierdo del operador es una columna única o una tupla.
Ejemplos:
SELECT entre corchetes.
Por compatibilidad histórica, cuando el lado derecho es una única expresión tuple, puede interpretarse como un conjunto de valores o como un único valor de tupla, dependiendo del lado izquierdo del operador IN. Si el lado izquierdo es un valor escalar, ClickHouse trata los elementos de esta única expresión tuple del lado derecho como valores IN independientes:
Query
Response
SELECT 1 IN (1, 2). Si el lado izquierdo también es una tupla, el lado derecho se interpreta como un conjunto de valores de tupla:
Query
Response
tuple simple. Un lado izquierdo escalar no puede compararse con un lado derecho que contenga múltiples valores de tupla:
Query
Response
IN.
En este caso, convierte el valor del lado derecho al tipo del lado izquierdo, como si la función accurateCastOrNull se aplicara al lado derecho.
Esto significa que el tipo de dato pasa a ser Nullable y, si la conversión
no puede realizarse, devuelve NULL.
Ejemplo
Query
Response
UserID IN users), esto equivale a la subconsulta UserID IN (SELECT * FROM users). Use esto cuando trabaje con datos externos que se envían junto con la consulta. Por ejemplo, la consulta puede enviarse junto con un conjunto de IDs de usuario cargados en la tabla temporal ‘users’, que debe filtrarse.
Si el lado derecho del operador es un nombre de tabla que utiliza el motor Set (un conjunto de datos preparado que siempre se mantiene en RAM), el conjunto de datos no se recreará en cada consulta.
La subconsulta puede especificar más de una columna para filtrar tuplas.
Ejemplo:
Query
IN deben ser del mismo tipo.
El operador IN y la subconsulta pueden aparecer en cualquier parte de la consulta, incluidas las funciones de agregación y las funciones lambda.
Ejemplo:
Query
Response
IN siempre se ejecuta una sola vez en un único servidor. No hay subconsultas dependientes.
Procesamiento de NULL
IN asume que el resultado de una operación con NULL siempre es igual a 0, independientemente de si NULL está en el lado derecho o izquierdo del operador. Los valores NULL no se incluyen en ningún conjunto de datos, no se corresponden entre sí y no pueden compararse si transform_null_in = 0.
A continuación se muestra un ejemplo con la tabla t_null:
SELECT x FROM t_null WHERE y IN (NULL,3), se obtiene el siguiente resultado:
y = NULL queda descartada de los resultados de la consulta. Esto se debe a que ClickHouse no puede determinar si NULL está incluido en el conjunto (NULL,3), devuelve 0 como resultado de la operación y SELECT excluye esta fila de la salida final.
Subconsultas distribuidas
IN con subconsultas (similares a los operadores JOIN): el IN / JOIN normal y el GLOBAL IN / GLOBAL JOIN. Se diferencian en cómo se ejecutan para el procesamiento distribuido de consultas.
Recuerda que los algoritmos descritos a continuación pueden funcionar de forma diferente según la opción de configuración
distributed_product_mode de Configuración.IN regular, la consulta se envía a los servidores remotos, y cada uno de ellos ejecuta las subconsultas en la cláusula IN o JOIN.
Al usar GLOBAL IN / GLOBAL JOIN, primero se ejecutan todas las subconsultas para GLOBAL IN / GLOBAL JOIN y los resultados se recopilan en tablas temporales. A continuación, las tablas temporales se envían a cada servidor remoto, donde las consultas se ejecutan con estos datos temporales.
En GLOBAL ... JOIN, el lado del join que se calcula como subconsulta depende del tipo de join: en los joins LEFT e INNER, se calcula la tabla derecha; en los joins RIGHT, en cambio, se calcula la tabla izquierda, ya que la tabla derecha es el lado preservado y debe leerse desde los segmentos.
Para una consulta no distribuida, utilice IN / JOIN de forma habitual.
Tenga cuidado al usar subconsultas en las cláusulas IN / JOIN para el procesamiento distribuido de consultas.
Veamos algunos ejemplos. Supongamos que cada servidor del clúster tiene una local_table normal. Cada servidor también tiene una tabla distributed_table de tipo Distributed, que abarca todos los servidores del clúster.
Para una consulta a la distributed_table, la consulta se enviará a todos los servidores remotos y se ejecutará en ellos mediante la local_table.
Por ejemplo, la consulta
IN:
- Cálculo de la intersección entre las audiencias de dos sitios.
IN se recopilará en cada servidor de forma independiente, considerando únicamente los datos almacenados localmente en cada uno de ellos.
Esto funcionará de forma correcta y óptima si se ha preparado para este caso y ha distribuido los datos entre los servidores del clúster de modo que los datos de un único UserID residan íntegramente en un único servidor. En ese caso, todos los datos necesarios estarán disponibles localmente en cada servidor. De lo contrario, el resultado será inexacto. A esta variante de la consulta la denominamos “local IN”.
Para corregir el funcionamiento de la consulta cuando los datos están distribuidos aleatoriamente entre los servidores del clúster, puede especificar distributed_table dentro de una subconsulta. La consulta quedaría así:
GLOBAL IN en lugar de IN. Veamos cómo funciona para la consulta:
_data1 se enviará a cada servidor remoto con la consulta (el nombre de la tabla temporal depende de la implementación).
Esto es más eficiente que usar el IN normal. Sin embargo, tenga en cuenta los siguientes puntos:
- Al crear una tabla temporal, no se eliminan los duplicados de los datos. Para reducir el volumen de datos transmitidos por la red, especifique DISTINCT en la subconsulta. (No necesita hacer esto para un
INnormal). - La tabla temporal se enviará a todos los servidores remotos. La transmisión no tiene en cuenta la topología de la red. Por ejemplo, si 10 servidores remotos residen en un centro de datos muy alejado del servidor que realiza la solicitud, los datos se enviarán 10 veces por el canal hacia el centro de datos remoto. Intente evitar conjuntos de datos grandes al usar
GLOBAL IN. - Al transmitir datos a servidores remotos, no es posible configurar restricciones de ancho de banda de red. Podría sobrecargar la red.
- Intente distribuir los datos entre los servidores para no tener que usar
GLOBAL INde forma habitual. - Si necesita usar
GLOBAL INcon frecuencia, planifique la ubicación del clúster de ClickHouse de modo que un único grupo de réplicas no resida en más de un centro de datos y que exista una red rápida entre ellas, para que una consulta pueda procesarse por completo dentro de un único centro de datos.
GLOBAL IN, en caso de que esa tabla local solo esté disponible en el servidor que realiza la solicitud y quiera usar sus datos en servidores remotos.
Subconsultas distribuidas y max_rows_in_set
max_rows_in_set y max_bytes_in_set para controlar la cantidad de datos que se transfiere durante las consultas distribuidas.
Esto es especialmente importante si la consulta GLOBAL IN devuelve una gran cantidad de datos. Considere el siguiente SQL:
some_predicate no es lo bastante selectivo, devolverá una gran cantidad de datos y causará problemas de rendimiento. En esos casos, conviene limitar la transferencia de datos por la red. Además, ten en cuenta que set_overflow_mode está configurado como throw (de forma predeterminada), lo que significa que se lanza una excepción cuando se alcanzan estos umbrales.
Subconsultas distribuidas y max_parallel_replicas
M está entre 1 y 3, según la réplica en la que se esté ejecutando la consulta local.
Estas opciones de configuración afectan a cada tabla de la familia MergeTree de la consulta y tienen el mismo efecto que aplicar SAMPLE 1/3 OFFSET (M-1)/3 a cada tabla.
Por lo tanto, añadir la opción de configuración max_parallel_replicas solo producirá resultados correctos si ambas tablas tienen el mismo esquema de replicación y se muestrean por UserID o por una subclave de este. En particular, si local_table_2 no tiene una clave de muestreo, se obtendrán resultados incorrectos. La misma regla se aplica a JOIN.
Una alternativa, si local_table_2 no cumple los requisitos, es usar GLOBAL IN o GLOBAL JOIN.
Si una tabla no tiene una clave de muestreo, se pueden usar opciones más flexibles para parallel_replicas_custom_key, que pueden dar lugar a un comportamiento diferente y más óptimo.