WITH RECURSIVE (Common Table Expressions) în PostgreSQL în PostgreSQL

Interogarea structurilor de date ierarhice (arbori organizaționali, categorii imbricate, grafuri de rețea) printr-o singură interogare SQL recursivă.

Sintaxă SQL
WITH RECURSIVE nume_cte AS (...) SELECT ...

În bazele de date relaționale, datele ierarhice (cum ar fi o structură de categorii cu sub-categorii la infinit sau o ierarhie de angajați și manageri) sunt dificil de extras: programatorii recurgeau adesea la zeci de interogări repetate din codul de aplicație (problema N+1).

Clauza WITH RECURSIVE permite definirea unui CTE (Common Table Expression) capabil să se auto-apeleze iterativ până la epuizarea tuturor nivelelor din arbore.

Structura constă din două părți legate prin `UNION ALL`: Termenul ancoră (interogarea de bază care selectează rădăcina arborelui) și Termenul recursiv (care face join între rândurile găsite la pasul anterior și restul tabelului), returnând întregul arbore ierarhic într-un singur tabel compact.

Sintaxă, opțiuni și concepte cheie

Opțiune / Concept Descriere practică
WITH RECURSIVE Cuvintele cheie obligatorii ce inițializează mecanismul iterativ de calcul.
Anchor Member (Termen ancoră) Interogarea inițială ce selectează primele înregistrări (ex. părintele de nivel 0).
Recursive Member (Termen recursiv) Interogarea ce leagă rezultatele precedente de noile nivele copil prin join.
UNION ALL Operatorul ce concatenează rezultatele fiecărei iterații până când nu se mai returnează rânduri noi.

Exemple practice și cod

Exemplul 1: Extragerea arborelui complet de categorii imbricate cu indicarea nivelului de adâncime

Cum se traversează o structură de tip arbore în SQL pur.

WITH RECURSIVE arbore_categorii AS (
    -- 1. Termenul ancoră: Categoriile principale (fără părinte)
    SELECT id, nume, parinte_id, 1 AS nivel
    FROM categorii
    WHERE parinte_id IS NULL

    UNION ALL

    -- 2. Termenul recursiv: Copiii categoriilor găsite anterior
    SELECT c.id, c.nume, c.parinte_id, ac.nivel + 1
    FROM categorii c
    INNER JOIN arbore_categorii ac ON c.parinte_id = ac.id
)
SELECT * FROM arbore_categorii ORDER BY nivel, nume;

-- Rezultat curat cu toate nivelele de sub-categorii:

Rezultat & Impact

Rezolvarea completă a ierarhiei într-o singură interogare executată nativ de motorul SQL.

Greșeli frecvente și bune practici

  • Crearea unei bucle infinite în date (cicluri unde A este părintele lui B, iar B este părintele lui A) fără o clauză de protecție pe adâncime (`WHERE nivel < 10`).
  • Utilizarea `UNION` în loc de `UNION ALL` fără un motiv întemeiat, ceea ce forțează eliminarea duplicatelor și încetinește enorm execuția.

Ghiduri și noțiuni înrudite