PeixeiroData

[1] SQL - CTE Recursiva

· Cezar Peixeiro

Para quem trabalha com dados a sintaxe das CTEs (Common Table Expression) é bem popular, porém uma funcionalidade, pouco explorada são as CTEs Recursivas. Elas fazem parte do ANSI SQL desde 1999, mas foram sendo adotadas de forma gradual pelos “dialetos” do sql com o passar dos anos.

Exemplos de possíveis aplicações dessa funcionalidade.

Tabelas onde hierarquias diferentes se misturam

Exemplo: Suponhamos que uma tabela possua um cadastro de funcionários até cargos de liderança. Nela temos o ID de cada um dos funcionários e o ID de seu gerente imediato.

id nome cargo id_mng
100 Jack mng 002
101 Bob dev 100
102 Carol dev 100
103 David dev 100
200 Eve mng 002
205 Frank dev 200
206 Grace dev 200

Vamos listar todos os funcionários que respondem ao gerente Jack (100):

** Este é um exemplo bem simples, que poderia ser resolvido de outra maneira. Apenas para demonstração a sintaxe.

 1WITH RECURSIVE depto_100 (id, nome, cargo) AS (
 2        SELECT
 3            id,
 4            nome,
 5            cargo
 6        FROM 
 7            funcionarios
 8        WHERE id = 100
 9
10        UNION ALL
11        SELECT 
12            e.id,
13            e.nome,
14            e.cargo
15        FROM 
16            funcionarios ASe
17        INNER JOIN depto AS de 
18            ON e.id_mng = de.id
19    )
20SELECT * FROM depto_100

É criado um SELECT de início, onde será baseada a interação e a consulta após o UNION ALL fará referência a própria CTE.

Outra aplicação interessante seria a criação de uma tabela calendário. No exemplo abaix crio uma tabela com duas colunas: data_inicio (primeiro dia do mês) e data_fim (último dia do mês) de maneira recursiva:

 1WITH RECURSIVE calendario AS (
 2    -- Base
 3    SELECT DATE '2023-01-01' AS data_inicio,
 4           LAST_DAY(DATE '2023-01-01') AS data_fim
 5
 6    UNION ALL
 7
 8    -- Recursão: próximos meses
 9    SELECT DATEADD(MONTH, 1, data_inicio) AS data_inicio,
10           LAST_DAY(DATEADD(MONTH, 1, data_inicio)) AS data_fim
11    FROM calendario
12    WHERE data_inicio < DATE '2025-12-01'
13)
14
15SELECT * FROM calendario;

Na parte BASE a coluna data_inicio foi definida como 2023-01-01 e a data_fim como o último dia relacionado ao mesmo mês do primeiro dia, utilizando LAST_DAY(). Na parte RECURSÃO, o é feito um SELECT na própria CTE, sempre adicionando 1 mês em data_inicio e data_fim, recursivamente. O loop recursivo finaliza quando data_inicio atinge um valor maior que 2025-12-01

A tabela resultante será algo como:

data_inicio data_fim
2023-02-01 2023-02-28
2023-03-01 2023-03-31
2023-04-01 2023-04-30
2023-05-01 2023-05-31
2023-06-01 2023-06-30
2023-07-01 2023-07-31
… até a última linha de …
2025-12-01 2025-12-31

#sql #cte #recursividade