0Pricing
SQL Academy · Aula

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 < 20 como 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_cycle e path.
  • 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

  1. Como funcionam as CTEs recursivas
  2. Percorrendo uma árvore de categorias
  3. Gerando séries e sequências
  4. Evitando laços infinitos
← Voltar para SQL Academy