No GoogleSQL para BigQuery, uma cláusula WITH
contém uma ou mais expressões de tabela comuns (CTEs, na sigla em inglês) com tabelas temporárias que podem ser referenciadas em uma expressão de consulta. Os CTEs podem ser não recursivos,
recursivos ou ambos. A palavra-chave RECURSIVE
permite a recursão na cláusula WITH
(WITH RECURSIVE
).
Um CTE recursivo pode referenciar a si mesmo, um CTE anterior ou um CTE subsequente. Um CTE não recursivo pode se referir apenas a CTEs anteriores e não pode se referir a si mesmo. Os CTEs recursivos são executados continuamente até que nenhum novo resultado seja encontrado, enquanto os CTEs não recursivos são executados uma vez. Por esses motivos, os CTEs recursivos são normalmente usados para consultar dados hierárquicos e de gráficos.
Por exemplo, considere um gráfico em que cada linha representa um nó que pode ser vinculado a outros nós. Para encontrar o fechamento transitivo de todos os nós alcançáveis de um
nó de início específico sem saber o número máximo de saltos, você precisará
de um CTE recursivo na consulta (WITH RECURSIVE
). A consulta recursiva começaria com o caso base do nó inicial, e cada etapa calcularia os novos nós não vistos que podem ser alcançados de todos os nós vistos até a etapa anterior. A consulta é concluída quando nenhum nó novo é encontrado.
No entanto, os CTEs recursivos podem ser caros em termos de computação. Portanto, antes de usá-los, leia este guia e a seção WITH
da
documentação de referência do GoogleSQL.
Criar um CTE recursivo
Para criar um CTE recursivo no GoogleSQL, use a cláusula
WITH RECURSIVE
, conforme mostrado no exemplo a seguir:
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
O exemplo anterior produz os seguintes resultados:
/*-----------*
| iteration |
+-----------+
| 1 |
| 1 |
| 2 |
| 2 |
| 3 |
| 3 |
*-----------*/
Um CTE recursivo inclui um termo base, um operador de união e um termo recursivo. O termo base executa a primeira iteração da operação de união recursiva. O termo recursivo executa as iterações restantes e precisa incluir uma autoreferência ao CTE recursivo. Somente o termo recursivo pode incluir uma autoreferência.
No exemplo anterior, o CTE recursivo contém os seguintes componentes:
- Nome recursivo do CTE:
CTE_1
- Termo de base:
SELECT 1 AS iteration
- Operador de união:
UNION ALL
- Termo recursivo:
SELECT iteration + 1 AS iteration FROM CTE_1 WHERE iteration < 3
Para saber mais sobre a sintaxe, as regras e os exemplos de CTE recursivos, consulte a cláusula
WITH
na documentação de referência do GoogleSQL.
Explorar a acessibilidade em um gráfico acíclico dirigido (DAG)
É possível usar uma consulta recursiva para explorar a acessibilidade em um gráfico acíclico dirigido (DAG). A consulta a seguir encontra todos os nós que podem ser alcançados do nó 5
em um gráfico chamado 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;
O exemplo anterior produz os seguintes resultados:
/*------*
| node |
+------+
| 5 |
| 6 |
| 7 |
| 8 |
*------*/
Resolver erros de limite de iteração
Os CTEs recursivos podem resultar em recursão infinita, que ocorre quando o termo recursivo é executado continuamente sem atender a uma condição de encerramento. Para encerrar recorrências infinitas, um limite de iterações para cada CTE recursivo é aplicado. Para o BigQuery, o limite de iteração é de 500 iterações. Quando um CTE recursivo atinge o número máximo de iterações, a execução do CTE é cancelada com um erro.
Esse limite existe porque a computação de um CTE recursivo pode ser cara, e executar um CTE com um grande número de iterações consome muitos recursos do sistema e leva muito mais tempo para ser concluído.
As consultas que alcançam o limite de iteração geralmente não têm uma condição de encerramento apropriada, criando um loop infinito ou usando CTEs recursivos em cenários inadequados.
Se ocorrer um erro no limite de iteração de recursão, veja as sugestões nesta seção.
Verificar a recursão infinita
Para evitar recursão infinita, verifique se o termo recursivo pode produzir um resultado vazio após executar um determinado número de iterações.
Uma maneira de verificar a recursão infinita é converter o CTE recursivo em um
TEMP TABLE
com uma repetição REPEAT
para as primeiras iterações de 100
, da seguinte maneira:
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;
Substitua os seguintes valores:
recursive_cte_name
: o CTE recursivo para depuração.base_expression
: o termo base do CTE recursivo.recursive_expression
: o termo recursivo do CTE recursivo.termination_condition_expression
: a expressão de encerramento do CTE recursivo.
Por exemplo, considere o seguinte CTE recursivo chamado TestCTE
:
WITH RECURSIVE
TestCTE AS (
SELECT 1 AS n
UNION ALL
SELECT n + 3 FROM TestCTE WHERE MOD(n, 6) != 0
)
Este exemplo usa os seguintes valores:
recursive_cte_name
:TestCTE
base_expression
:SELECT 1
recursive_expression
:n + 3
termination_condition_expression
:MOD(n, 6) != 0
O código abaixo testaria o TestCTE
em busca de recursão 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;
O exemplo anterior produz os resultados a seguir, que incluem o ID da iteração e o número de linhas que foram produzidas durante essa iteração:
/*-----------+----------*
| 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 |
*-----------+----------*/
Estes são os resultados reais produzidos durante a iteração 2
:
/*----------+-----------*
| n | iteration |
+----------+-----------+
| 7 | 2 |
*----------+-----------*/
Se o número de linhas for sempre maior que zero, o que é verdadeiro neste exemplo, a amostra provavelmente terá uma recursão infinita.
Verificar o uso adequado do CTE recursivo
Verifique se você está usando o CTE recursivo em um cenário adequado.
Os CTEs recursivos podem ser caros de computar, porque foram projetados para consultar
dados hierárquicos e dados de gráfico. Se você não estiver consultando esses dois tipos de
dados, considere alternativas, como o uso da
instrução LOOP
com um CTE não recursivo.
Dividir o CTE recursivo em vários CTEs recursivos
Se você acha que seu CTE recursivo precisa de mais do que o número máximo de iterações permitidas, você pode dividi-lo em vários CTEs recursivos.
É possível dividir um CTE recursivo com uma estrutura de consulta semelhante a esta:
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 [...]
Substitua os seguintes valores:
base_expression
: o termo de base de termo recursivo para o CTE atual.recursive_expression
: a expressão de termo recursivo para o CTE atual.
Por exemplo, o código abaixo divide um CTE em três CTEs distintos:
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
No exemplo anterior, 500 iterações foram substituídas por 10 iterações. Assim, é mais rápido ver os resultados da consulta. A consulta produz 30 linhas, mas cada CTE recursivo faz iterações apenas 10 vezes. A saída será assim:
/*-----------*
| iteration |
+-----------+
| 2 |
| ... |
| 30 |
*-----------*/
É possível testar a consulta anterior em iterações muito maiores.
Usar um loop em vez de um CTE recursivo
Para evitar limites de iteração, considere usar um loop em vez de um CTE recursivo.
É possível criar uma repetição com uma das várias instruções de loop,
como LOOP
, REPEAT
ou WHILE
. Para mais informações, consulte
Repetições.
Mudar o limite recursivo
Se você acredita que os seguintes fatores se aplicam, entre em contato com o Atendimento ao cliente para aumentar o limite recursivo:
- Se você tiver um motivo válido para o CTE recursivo executar mais de 500 iterações.
- Você está de acordo com uma execução muito mais longa.
Lembre-se de que aumentar o limite recursivo pode causar riscos:
- O CTE pode falhar com uma mensagem de erro diferente, como memória excedida ou tempo limite.
- Se o projeto estiver usando o modelo de preços sob demanda, o CTE ainda poderá falhar com um erro no nível de faturamento até que você mude para o modelo de preços por capacidade.
- Um CTE recursivo com um grande número de iterações consome muitos recursos. Isso pode afetar outras consultas em execução na mesma reserva, já que elas competem por recursos compartilhados.
Preços
Se você usa o faturamento sob demanda, o BigQuery cobra com base no número de bytes processados durante a execução de uma consulta com um CTE recursivo.
Para mais informações, consulte Cálculo do tamanho da consulta.
Cotas
Para informações sobre cotas e limites de CTE recursivos, consulte Cotas e limites.