En GoogleSQL para BigQuery, una cláusula WITH
contiene una o más expresiones de tabla comunes (CTE) con tablas temporales a las que puedes hacer referencia en una expresión de consulta. Las CTE pueden ser no recurrentes, recurrentes o ambas. La palabra clave RECURSIVE
habilita la recurrencia en la cláusula WITH
(WITH RECURSIVE
).
Una CTE recurrente puede hacer referencia a sí misma, a una CTE anterior o a una CTE posterior. Una CTE no recurrente puede hacer referencia solo a CTE anteriores y no puede hacer referencia a sí misma. Las CTE recurrentes se ejecutan de forma continua hasta que no se encuentren resultados nuevos, mientras que las CTE no recurrentes se ejecutan una vez. Por estas razones, las CTE recurrentes se suelen usar para consultar datos jerárquicos y datos de gráficos.
Por ejemplo, considera un gráfico en el que cada fila representa un nodo que puede vincularse con otros nodos. Para encontrar el cierre transitivo de todos los nodos accesibles de un nodo de inicio en particular sin conocer la cantidad máxima de saltos, necesitarías una CTE recurrente en la consulta (WITH RECURSIVE
). La consulta recurrente comenzaría con el caso base del nodo de inicio, y en cada paso se calcularían los nodos nuevos no vistos que se pueden alcanzar desde todos los nodos vistos hasta el paso anterior. La consulta finaliza cuando no se pueden encontrar nodos nuevos.
Sin embargo, las CTE recurrentes pueden ser costosas en términos de procesamiento, por lo que, antes de usarlas, revisa esta guía y la sección WITH
de la documentación de referencia de GoogleSQL.
Crea una CTE recurrente
Para crear una CTE recurrente en GoogleSQL, usa la cláusula WITH RECURSIVE
, como se muestra en el siguiente ejemplo:
WITH RECURSIVE
CTE_1 AS (
(SELECT 1 AS iteration UNION ALL SELECT 1 AS iteration)
UNION ALL
SELECT iteration + 1 AS iteration FROM CTE_1 WHERE iteration < 3
)
SELECT iteration FROM CTE_1
ORDER BY 1 ASC
En el ejemplo anterior, se producen los siguientes resultados:
/*-----------*
| iteration |
+-----------+
| 1 |
| 1 |
| 2 |
| 2 |
| 3 |
| 3 |
*-----------*/
Una CTE recurrente incluye un término base, un operador de unión y un término recurrente. El término base ejecuta la primera iteración de la operación de unión recurrente. El término recurrente ejecuta las iteraciones restantes y debe incluir una autorreferencia a la CTE recurrente. Solo el término recurrente puede incluir una autorreferencia.
En el ejemplo anterior, la CTE recurrente contiene los siguientes componentes:
- Nombre de la CTE recurrente:
CTE_1
- Término base:
SELECT 1 AS iteration
- Operador de unión:
UNION ALL
- Término recurrente:
SELECT iteration + 1 AS iteration FROM CTE_1 WHERE iteration < 3
Para obtener más información sobre la sintaxis, las reglas y los ejemplos de la CTE recurrente, consulta la cláusula WITH
en la documentación de referencia de GoogleSQL.
Explora la accesibilidad en un grafo acíclico dirigido (DAG)
Puedes usar una consulta recurrente para explorar la accesibilidad en un grafo acíclico dirigido (DAG). En la siguiente consulta, se encuentran todos los nodos a los que se puede acceder desde el nodo 5
en un grafo llamado GraphData
:
WITH RECURSIVE
GraphData AS (
-- 1 5
-- / \ / \
-- 2 - 3 6 7
-- | \ /
-- 4 8
SELECT 1 AS from_node, 2 AS to_node UNION ALL
SELECT 1, 3 UNION ALL
SELECT 2, 3 UNION ALL
SELECT 3, 4 UNION ALL
SELECT 5, 6 UNION ALL
SELECT 5, 7 UNION ALL
SELECT 6, 8 UNION ALL
SELECT 7, 8
),
R AS (
(SELECT 5 AS node)
UNION ALL
(
SELECT GraphData.to_node AS node
FROM R
INNER JOIN GraphData
ON (R.node = GraphData.from_node)
)
)
SELECT DISTINCT node FROM R ORDER BY node;
En el ejemplo anterior, se producen los siguientes resultados:
/*------*
| node |
+------+
| 5 |
| 6 |
| 7 |
| 8 |
*------*/
Soluciona problemas de errores de límite de iteración
Las CTE recurrentes pueden generar una recurrencia infinita, lo que ocurre cuando el término recurrente se ejecuta de forma continua sin cumplir una condición de finalización. A fin de finalizar las recurrencias infinitas, se aplica un límite de iteraciones para cada CTE recurrente. Para BigQuery, el límite de iteraciones es de 500 iteraciones. Una vez que una CTE recurrente alcanza la cantidad máxima de iteraciones, la ejecución de la CTE se anula con un error.
Este límite existe porque el procesamiento de una CTE recurrente puede ser costoso, y la ejecución de un CTE con una gran cantidad de iteraciones consume muchos recursos del sistema y lleva mucho más tiempo para finalizar.
Por lo general, las consultas que alcanzan el límite de iteración no tienen una condición de finalización adecuada, por lo que se crea un bucle infinito o se usan CTE recurrentes en situaciones inadecuadas.
Si experimentas un error de límite de iteraciones de recurrencia, revisa las sugerencias de esta sección.
Verifica la recurrencia infinita
Para evitar la recurrencia infinita, asegúrate de que el término recurrente pueda producir un resultado vacío después de ejecutar una cierta cantidad de iteraciones.
Una forma de verificar la recurrencia infinita es convertir tu CTE recurrente en una TEMP TABLE
con un bucle REPEAT
durante las primeras 100
iteraciones, de la siguiente manera:
DECLARE current_iteration INT64 DEFAULT 0; CREATE TEMP TABLE recursive_cte_name AS SELECT base_expression, current_iteration AS iteration; REPEAT SET current_iteration = current_iteration + 1; INSERT INTO recursive_cte_name SELECT recursive_expression, current_iteration FROM recursive_cte_name WHERE termination_condition_expression AND iteration = current_iteration - 1 AND current_iteration < 100; UNTIL NOT EXISTS(SELECT * FROM recursive_cte_name WHERE iteration = current_iteration) END REPEAT;
Reemplaza los siguientes valores:
recursive_cte_name
: la CTE recurrente que se depurará.base_expression
: el término base de la CTE recurrente.recursive_expression
: el término recurrente de la CTE recurrente.termination_condition_expression
: La expresión de finalización de la CTE recurrente.
Por ejemplo, considera la siguiente CTE recursiva llamada TestCTE
:
WITH RECURSIVE
TestCTE AS (
SELECT 1 AS n
UNION ALL
SELECT n + 3 FROM TestCTE WHERE MOD(n, 6) != 0
)
En este ejemplo, se usan los siguientes valores:
recursive_cte_name
:TestCTE
base_expression
:SELECT 1
recursive_expression
:n + 3
termination_condition_expression
:MOD(n, 6) != 0
Por lo tanto, el siguiente código probaría el TestCTE
para la recurrencia infinita:
DECLARE current_iteration INT64 DEFAULT 0; CREATE TEMP TABLE TestCTE AS SELECT 1 AS n, current_iteration AS iteration; REPEAT SET current_iteration = current_iteration + 1; INSERT INTO TestCTE SELECT n + 3, current_iteration FROM TestCTE WHERE MOD(n, 6) != 0 AND iteration = current_iteration - 1 AND current_iteration < 10; UNTIL NOT EXISTS(SELECT * FROM TestCTE WHERE iteration = current_iteration) END REPEAT; -- Print the number of rows produced by each iteration SELECT iteration, COUNT(1) AS num_rows FROM TestCTE GROUP BY iteration ORDER BY iteration; -- Examine the actual result produced for a specific iteration SELECT * FROM TestCTE WHERE iteration = 2;
El ejemplo anterior produce los siguientes resultados, que incluyen el ID de iteración y la cantidad de filas que se produjeron durante esa iteración:
/*-----------+----------*
| iteration | num_rows |
+-----------+----------+
| 0 | 1 |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 6 | 1 |
| 7 | 1 |
| 8 | 1 |
| 9 | 1 |
| 10 | 1 |
*-----------+----------*/
Estos son los resultados reales que se generaron durante la iteración 2
:
/*----------+-----------*
| n | iteration |
+----------+-----------+
| 7 | 2 |
*----------+-----------*/
Si la cantidad de filas siempre es mayor que cero, lo que es verdadero en este ejemplo, es probable que la muestra tenga una recurrencia infinita.
Verifica el uso correcto de la CTE recurrente
Verifica que usas la CTE recurrente en una situación adecuada.
Las CTE recurrentes pueden ser costosas de procesar, ya que están diseñadas para consultar datos de jerarquía y datos de grafos. Si no consultas estos dos tipos de datos, considera alternativas, como usar la sentencia LOOP
con una CTE no recurrente.
Divide un CTE recurrente en varias CTE recurrentes
Si crees que tu CTE recurrente necesita más de la cantidad máxima de iteraciones permitidas, es posible que puedas dividir tu CTE recurrente en varias CTE recurrentes.
Puedes dividir una CTE recurrente con una estructura de consulta similar a la que se muestra a continuación:
WITH RECURSIVE CTE_1 AS ( SELECT base_expression UNION ALL SELECT recursive_expression FROM CTE_1 WHERE iteration < 500 ), CTE_2 AS ( SELECT * FROM CTE_1 WHERE iteration = 500 UNION ALL SELECT recursive_expression FROM CTE_2 WHERE iteration < 500 * 2 ), CTE_3 AS ( SELECT * FROM CTE_2 WHERE iteration = 500 * 2 UNION ALL SELECT recursive_expression FROM CTE_3 WHERE iteration < 500 * 3 ), [, ...] SELECT * FROM CTE_1 UNION ALL SELECT * FROM CTE_2 WHERE iteration > 500 UNION ALL SELECT * FROM CTE_3 WHERE iteration > 500 * 2 [...]
Reemplaza los siguientes valores:
base_expression
: la expresión del término base para la CTE actual.recursive_expression
: la expresión de término recurrente para la CTE actual.
Por ejemplo, el siguiente código divide una CTE en tres CTE distintas:
WITH RECURSIVE
CTE_1 AS (
SELECT 1 AS iteration
UNION ALL
SELECT iteration + 1 AS iteration FROM CTE_1 WHERE iteration < 10
),
CTE_2 AS (
SELECT * FROM CTE_1 WHERE iteration = 10
UNION ALL
SELECT iteration + 1 AS iteration FROM CTE_2 WHERE iteration < 10 * 2
),
CTE_3 AS (
SELECT * FROM CTE_2 WHERE iteration = 10 * 2
UNION ALL
SELECT iteration + 1 AS iteration FROM CTE_3 WHERE iteration < 10 * 3
)
SELECT iteration FROM CTE_1
UNION ALL
SELECT iteration FROM CTE_2 WHERE iteration > 10
UNION ALL
SELECT iteration FROM CTE_3 WHERE iteration > 20
ORDER BY 1 ASC
En el ejemplo anterior, 500 iteraciones se reemplazan por 10 iteraciones para que sea más rápido ver los resultados de la consulta. La consulta produce 30 filas, pero cada CTE recurrente solo itera 10 veces. El resultado será similar a esto:
/*-----------*
| iteration |
+-----------+
| 2 |
| ... |
| 30 |
*-----------*/
Puedes probar la consulta anterior en iteraciones mucho más grandes.
Usa un bucle en lugar de una CTE recurrente
Para evitar los límites de iteración, considera usar un bucle en lugar de una CTE recurrente.
Puedes crear un bucle con una de varias declaraciones de bucle, como LOOP
, REPEAT
o WHILE
. Para obtener más información, consulta Bucles.
Cambia el límite recurrente
Si crees que se aplican los siguientes factores, comunícate con el servicio de atención al cliente para aumentar el límite recurrente:
- Si tienes una razón válida para que tu CTE recurrente ejecute más de 500 iteraciones.
- Si estás de acuerdo con una ejecución mucho más larga.
Ten en cuenta que aumentar el límite recurrente tiene posibles riesgos:
- Tu CTE puede fallar con un mensaje de error diferente, por ejemplo, de que se superó el límite de memoria o se agotó el tiempo de espera.
- Si tu proyecto usa el modelo de precios on demand, es posible que tu CTE falle de todas formas con un error de nivel de facturación hasta que cambies al modelo de precios basado en la capacidad.
- Una CTE recurrente con una gran cantidad de iteraciones consume muchos recursos y esto puede afectar a otras consultas que se ejecutan dentro de la misma reserva, ya que compiten por los recursos compartidos.
Precios
Si usas la facturación on demand, BigQuery cobra según la cantidad de bytes que se procesan durante la ejecución de una consulta con una CTE recurrente.
Para obtener más información, consulta Cálculo del tamaño de la consulta.
Cuotas
Para obtener más información sobre las cuotas y los límites de CTE recurrentes, consulta Cuotas y límites.