0Pricing
SQL Academy · Aula

Percorrendo uma árvore de categorias

Expanda completamente as árvores de pai e filho.

Percorrendo uma árvore de categorias é uma aula grátis de SQL Academy no CoddyKit. Esta é a aula 2 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 que é uma árvore de categorias?

Muitos conjuntos de dados do mundo real têm uma relação entre pai e filho. Um catálogo de produtos pode ter categorias como Eletrônicos → Telefones → Telefones inteligentes. Cada nó tem um pai, formando uma estrutura em árvore.

No SQL, isso normalmente é armazenado como uma tabela autorreferente: cada linha tem um id e um parent_id que aponta para outra linha da mesma tabela.

CREATE TABLE categories (
  id       INT PRIMARY KEY,
  name     VARCHAR(100) NOT NULL,
  parent_id INT REFERENCES categories(id)
);

Dados de exemplo de categorias

Vamos preencher uma pequena árvore de categorias. O nó raiz tem parent_id = NULL porque não tem pai. Todos os outros nós apontam para seu pai por meio de um parent_id não nulo.

INSERT INTO categories (id, name, parent_id) VALUES
  (1, 'Electronics',   NULL),
  (2, 'Phones',         1),
  (3, 'Laptops',        1),
  (4, 'Smartphones',    2),
  (5, 'Feature Phones', 2),
  (6, 'Gaming Laptops', 3),
  (7, 'Ultrabooks',     3);

O problema das consultas simples

Um SELECT simples só consegue buscar um nível por vez. Para alcançar três níveis de profundidade, seriam necessárias três consultas separadas ou três junções autorreferentes, o que se torna inviável à medida que a árvore cresce.

WITH RECURSIVE resolve esse problema ao permitir que uma consulta faça referência à própria saída, percorrendo a árvore nível a nível até que nenhuma nova linha seja encontrada.

-- This only shows direct children of Electronics (level 1)
SELECT id, name
FROM   categories
WHERE  parent_id = 1;

Anatomia de WITH RECURSIVE

Uma CTE recursiva tem duas partes separadas por UNION ALL:

1. Membro âncora — um SELECT normal que fornece as linhas iniciais.

2. Membro recursivo — um SELECT que faz uma junção da CTE consigo mesma, produzindo o próximo nível a cada iteração.

O mecanismo repete o membro recursivo até que ele não retorne nenhuma linha.

WITH RECURSIVE cte AS (
  -- Anchor: starting rows
  SELECT ...
  UNION ALL
  -- Recursive: join cte to base table
  SELECT ... FROM base_table JOIN cte ON ...
)
SELECT * FROM cte;

Percorrendo a árvore completa a partir da raiz

Comece pela raiz (onde parent_id IS NULL) e percorra todos os descendentes. O membro recursivo faz uma junção de cada linha acumulada novamente com categories, usando a relação entre pai e filho.

WITH RECURSIVE category_tree AS (
  -- Anchor: root nodes
  SELECT id, name, parent_id, 1 AS depth
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  -- Recursive: children of current level
  SELECT c.id, c.name, c.parent_id, ct.depth + 1
  FROM   categories      c
  JOIN   category_tree   ct ON ct.id = c.parent_id
)
SELECT id, name, depth
FROM   category_tree
ORDER  BY depth, id;

Rastreando o caminho

É útil registrar o caminho completo da raiz até cada nó. Podemos construir uma cadeia de caracteres path concatenando os nomes dos ancestrais à medida que avançamos na recursão.

Assim, fica fácil exibir trilhas de navegação como Eletrônicos / Telefones / Smartphones.

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id,
         name AS path
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id,
         ct.path || ' / ' || c.name
  FROM   categories    c
  JOIN   category_tree ct ON ct.id = c.parent_id
)
SELECT id, name, path
FROM   category_tree
ORDER  BY path;

Começando por um nó específico

Não é necessário começar pela raiz. Alterando a cláusula WHERE da âncora, você pode percorrer a subárvore de qualquer nó. Aqui, começamos por Telefones (id = 2) e recuperamos todos os seus descendentes.

WITH RECURSIVE subtree AS (
  SELECT id, name, parent_id, 0 AS depth
  FROM   categories
  WHERE  id = 2          -- start at Phones

  UNION ALL

  SELECT c.id, c.name, c.parent_id, s.depth + 1
  FROM   categories c
  JOIN   subtree    s ON s.id = c.parent_id
)
SELECT id, name, depth
FROM   subtree
ORDER  BY depth, id;

Percorrendo para cima: encontrando todos os ancestrais

A árvore também pode ser percorrida no sentido inverso — subindo de uma folha até a raiz. Basta inverter a junção para seguir parent_id para cima, em vez de para baixo. Isso é útil quando você precisa da trilha de navegação completa para um nó folha conhecido.

WITH RECURSIVE ancestors AS (
  SELECT id, name, parent_id
  FROM   categories
  WHERE  id = 4          -- start at Smartphones

  UNION ALL

  SELECT c.id, c.name, c.parent_id
  FROM   categories c
  JOIN   ancestors  a ON a.parent_id = c.id
)
SELECT id, name
FROM   ancestors
ORDER  BY id;

Adicionando uma exibição indentada

Um padrão comum de interface do usuário é indentar visualmente os nós filhos. Podemos usar REPEAT (ou LPAD) junto com a coluna depth para prefixar cada nome com espaços, produzindo uma visualização de árvore baseada em texto.

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id, 0 AS depth
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id, ct.depth + 1
  FROM   categories    c
  JOIN   category_tree ct ON ct.id = c.parent_id
)
SELECT
  REPEAT('    ', depth) || name AS indented_name,
  depth
FROM   category_tree
ORDER  BY path;

Evitando laços infinitos

Se os seus dados contiverem um ciclo (A é pai de B, B é pai de A), a recursão será executada indefinidamente e causará uma falha. Você pode evitar isso mantendo os identificadores visitados em um vetor e interrompendo a execução quando o identificador atual já estiver presente.

WITH RECURSIVE safe_tree AS (
  SELECT id, name, parent_id,
         ARRAY[id] AS visited
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id,
         st.visited || c.id
  FROM   categories c
  JOIN   safe_tree  st ON st.id = c.parent_id
  WHERE  c.id <> ALL(st.visited)   -- stop if already seen
)
SELECT id, name FROM safe_tree;

Contando os descendentes de cada nó

Depois de obter a árvore completa, você pode agregá-la. Aqui, contamos quantos descendentes cada nó tem, agrupando as linhas filhas com base na lista de ancestrais. Isso é útil para exibir a quantidade de itens ao lado dos nomes das categorias em um menu de navegação.

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id, id AS root_id
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id, ct.root_id
  FROM   categories    c
  JOIN   category_tree ct ON ct.id = c.parent_id
)
SELECT
  root_id,
  COUNT(*) - 1 AS descendant_count
FROM   category_tree
GROUP  BY root_id
ORDER  BY root_id;

Verificação rápida

Teste sua compreensão das consultas recursivas para árvores de categorias.

Recapitulação da lição

Nesta lição, você aprendeu a percorrer uma tabela de categorias autorreferenciada usando WITH RECURSIVE.

Principais aprendizados:

- O membro âncora seleciona os nós iniciais (geralmente a raiz).

- O membro recursivo faz uma junção da CTE novamente com a tabela base para encontrar o próximo nível.

- Adicione uma coluna depth para acompanhar a quantidade de níveis de profundidade de cada nó.

- Construa uma cadeia de caracteres path para gerar trilhas de navegação.

- Percorra para cima seguindo parent_id no sentido inverso para encontrar todos os ancestrais.

- Use um vetor visited para evitar ciclos em dados inconsistentes.

Perguntas Frequentes

A aula “Percorrendo uma árvore de categorias” é grátis?

Sim — o texto completo de “Percorrendo uma árvore de categorias” é 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 “Percorrendo uma árvore de categorias”?

Expanda completamente as árvores de pai e filho. 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 2 de 4.

Quanto tempo leva a aula “Percorrendo uma árvore de categorias”?

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