Evitando laços infinitos
Limites de profundidade e detecção de ciclos.
Evitando laços infinitos é uma aula grátis de SQL Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de SQL Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de SQL Academy inclui 4 aulas no total.
O problema do laço infinito
As CTEs recursivas são poderosas, mas envolvem um risco sério: se a sua consulta nunca chegar a um caso base, ela ficará em um laço infinito, consumindo toda a memória disponível e encerrando a sessão do banco de dados.
Entender por que os laços infinitos acontecem é o primeiro passo para evitá-los.
Quando um laço nunca termina?
Uma CTE recursiva entra em um laço indefinido quando o termo recursivo continua produzindo novas linhas sem nunca chegar a um estado em que nenhuma nova linha seja gerada.
Isso geralmente acontece em dois cenários: uma condição de término ausente ou incorreta, ou dados cíclicos em que o nó A aponta para B e B aponta de volta para A.
-- Simple recursive CTE that WOULD loop forever
-- (do NOT run this as-is; illustration only)
WITH RECURSIVE counter AS (
SELECT 1 AS n -- base case
UNION ALL
SELECT n + 1 -- recursive term
FROM counter
-- no WHERE clause to stop it!
)
SELECT n FROM counter;Adicionando um limite de profundidade
A proteção mais simples é um contador de profundidade. Adicione uma coluna que seja incrementada em 1 a cada etapa recursiva e pare quando ela ultrapassar uma profundidade máxima.
Isso garante o término independentemente dos dados, e o limite escolhido fornece um teto de segurança.
WITH RECURSIVE counter AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1
FROM counter
WHERE n < 10 -- stop at depth 10
)
SELECT n FROM counter;Limite de profundidade em uma consulta de hierarquia
Ao percorrer uma hierarquia de funcionários, você pode acompanhar a profundidade junto com o caminho. A cláusula WHERE depth < 5 impede o percurso além de 5 níveis, mesmo que os dados contenham ligações mais profundas ou circulares.
CREATE TEMP TABLE employees (
id INT PRIMARY KEY,
name TEXT,
manager_id INT
);
INSERT INTO employees VALUES
(1, 'Alice', NULL),
(2, 'Bob', 1),
(3, 'Carol', 2),
(4, 'Dave', 3);
WITH RECURSIVE hierarchy AS (
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL -- root
UNION ALL
SELECT e.id, e.name, e.manager_id, h.depth + 1
FROM employees e
JOIN hierarchy h ON e.manager_id = h.id
WHERE h.depth < 5 -- depth limit
)
SELECT id, name, depth FROM hierarchy ORDER BY depth, id;O que é detecção de ciclos?
Um ciclo ocorre em dados de grafo quando seguir as arestas acaba levando a um nó que você já visitou. Por exemplo: A → B → C → A.
Um limite de profundidade ainda encerra a consulta em dados cíclicos, mas não informa onde está o ciclo. A detecção explícita de ciclos informa.
CREATE TEMP TABLE edges (
from_node INT,
to_node INT
);
-- Introduce a cycle: 1->2->3->1
INSERT INTO edges VALUES
(1, 2),
(2, 3),
(3, 1), -- cycle back to 1
(1, 4); -- also a non-cyclic branch
SELECT * FROM edges;Rastreando nós visitados com um vetor
Uma técnica robusta de detecção de ciclos consiste em transportar um vetor de identificadores dos nós visitados durante a recursão. Antes de visitar o próximo nó, verifique se ele já está no vetor. Se estiver, ignore-o.
O PostgreSQL facilita isso com o operador ANY(array) e o operador || de inclusão em vetor.
WITH RECURSIVE traverse AS (
-- Start from node 1
SELECT from_node,
to_node,
ARRAY[from_node] AS visited
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node,
e.to_node,
t.visited || e.from_node
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
WHERE NOT (e.from_node = ANY(t.visited)) -- skip visited nodes
)
SELECT from_node, to_node, visited
FROM traverse;A cláusula CYCLE (PostgreSQL 14+)
O PostgreSQL 14 introduziu uma cláusula CYCLE integrada para CTEs recursivas. Ela adiciona automaticamente duas colunas: um sinalizador booleano que é true quando um ciclo é detectado e um vetor que registra o caminho percorrido.
Isso é mais simples do que manter o vetor manualmente.
WITH RECURSIVE traverse AS (
SELECT from_node, to_node
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node, e.to_node
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
)
CYCLE from_node SET is_cycle USING path
SELECT from_node, to_node, is_cycle, path
FROM traverse;Combinando limite de profundidade e detecção de ciclos
Usar um limite de profundidade e a detecção de ciclos em conjunto oferece a melhor garantia de segurança:
- O limite de profundidade funciona como um teto rígido, independentemente da qualidade dos dados.
- A detecção de ciclos interrompe a execução assim que um laço é encontrado, economizando iterações desnecessárias.
Em consultas de produção, aplique sempre pelo menos uma dessas proteções.
WITH RECURSIVE traverse AS (
SELECT from_node,
to_node,
1 AS depth,
ARRAY[from_node] AS visited
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node,
e.to_node,
t.depth + 1,
t.visited || e.from_node
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
WHERE t.depth < 10 -- depth limit
AND NOT (e.from_node = ANY(t.visited)) -- cycle guard
)
SELECT from_node, to_node, depth, visited
FROM traverse;Construindo o caminho completo como uma cadeia de caracteres
Além da detecção de ciclos, é útil registrar o caminho completo do percurso como uma cadeia de caracteres legível. Concatenar os identificadores dos nós separados por -> facilita exibir ou depurar a rota percorrida pelo grafo.
WITH RECURSIVE traverse AS (
SELECT from_node,
to_node,
1 AS depth,
ARRAY[from_node] AS visited,
from_node::TEXT AS path_str
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node,
e.to_node,
t.depth + 1,
t.visited || e.from_node,
t.path_str || ' -> ' || e.from_node::TEXT
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
WHERE t.depth < 10
AND NOT (e.from_node = ANY(t.visited))
)
SELECT from_node, to_node, path_str, depth
FROM traverse
ORDER BY depth;Configurando o limite máximo de iterações recursivas
Alguns bancos de dados (MariaDB, versões antigas do MySQL) usam uma variável de sessão para limitar a recursão. No PostgreSQL, a abordagem equivalente é contar com o contador de profundidade que você escreve ou usar limites de tempo no nível da instrução.
Definir um statement_timeout é uma última camada de segurança que encerra qualquer consulta fora de controle após um período determinado.
-- PostgreSQL: set a statement timeout as a safety net
SET statement_timeout = '5s';
-- Now any query that runs longer than 5 seconds is cancelled
WITH RECURSIVE counter AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM counter WHERE n < 1000000
)
SELECT MAX(n) FROM counter;
-- Reset to default when done
SET statement_timeout = '0';Escolhendo o limite de profundidade adequado
Não existe um limite de profundidade universal. Escolha o seu com base na profundidade máxima realista dos seus dados:
- Um organograma raramente ultrapassa 10–15 níveis — use
depth < 20como uma margem confortável. - Uma árvore do sistema de arquivos pode chegar a 50–100 níveis de profundidade.
- Um percurso em grafo de rede social costuma ser limitado a 3–6 saltos.
Defina um limite alto o suficiente para abranger dados válidos, mas baixo o suficiente para detectar consultas fora de controle rapidamente.
-- Example: org chart with a generous but safe depth cap
WITH RECURSIVE org AS (
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id, o.depth + 1
FROM employees e
JOIN org o ON e.manager_id = o.id
WHERE o.depth < 20 -- realistic upper bound for an org chart
)
SELECT id, name, depth
FROM org
ORDER BY depth, name;Limites de profundidade versus detecção de ciclos
Qual técnica você deve usar?
Recapitulação: mantendo consultas recursivas seguras
Veja um resumo do que você aprendeu sobre como evitar laços infinitos em CTEs recursivas:
- Limite de profundidade — adicione uma coluna de contador e pare com
WHERE depth < N. Sempre eficaz e fácil de implementar. - Detecção de ciclos baseada em vetor — transporte os identificadores dos nós visitados em um vetor e ignore qualquer nó que já esteja nele. Para na primeira ocorrência de um ciclo.
- Cláusula CYCLE (PostgreSQL 14+) — sintaxe integrada que automatiza o rastreamento de ciclos com as colunas
is_cycleepath. - Tempo limite da instrução — uma camada de segurança no nível do banco de dados para consultas fora de controle, não um substituto para uma lógica adequada.
- Combine ambos: o limite de profundidade e a detecção de ciclos em produção para obter a maior garantia.
Com essas técnicas, você pode percorrer hierarquias e grafos com segurança, sem correr o risco de travamentos do banco de dados.
Perguntas Frequentes
A aula “Evitando laços infinitos” é grátis?
Sim — o texto completo de “Evitando laços infinitos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de SQL Academy, atualize para CoddyKit PRO. O curso de SQL Academy inclui 4 aulas no total.
O que vou aprender em “Evitando laços infinitos”?
Limites de profundidade e detecção de ciclos. Você pratica SQL Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar SQL Academy?
Nenhuma experiência prévia é necessária. SQL Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Evitando laços infinitos”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de SQL Academy?
Sim. Cada aula de SQL Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Como funcionam as CTEs recursivas
- Percorrendo uma árvore de categorias
- Gerando séries e sequências
- Evitando laços infinitos