PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Query ricorsiva Postgres con row_to_json

Scusa per la risposta molto tardiva, ma penso di aver trovato una soluzione elegante che potrebbe diventare una risposta accettata per questa domanda.

Basandomi sul fantastico "piccolo trucco" trovato da @pozs, ho trovato una soluzione che:

  • risolve la situazione delle "foglie canaglia" con pochissimo codice (sfruttando il NOT EXISTS predicato)
  • evita il calcolo/condizione di livello intero
WITH RECURSIVE customer_area_tree("id", "customer_id", "parent_id", "name", "description", "children") AS (
  -- tree leaves (no matching children)
  SELECT c.*, json '[]'
  FROM customer_area_node c
  WHERE NOT EXISTS(SELECT * FROM customer_area_node AS hypothetic_child WHERE hypothetic_child.parent_id = c.id)

  UNION ALL

  -- pozs's awesome "little hack"
  SELECT (parent).*, json_agg(child) AS "children"
  FROM (
    SELECT parent, child
    FROM customer_area_tree AS child
    JOIN customer_area_node parent ON parent.id = child.parent_id
  ) branch
  GROUP BY branch.parent
)
SELECT json_agg(t)
FROM customer_area_tree t
LEFT JOIN customer_area_node AS hypothetic_parent ON(hypothetic_parent.id = t.parent_id)
WHERE hypothetic_parent.id IS NULL

Aggiorna :

Testato con dati molto semplici, funziona, ma come ha sottolineato posz in un commento, con i suoi dati di esempio, alcuni nodi foglia non autorizzati vengono dimenticati. Ma ho scoperto che con dati ancora più complessi, anche la risposta precedente non funziona, perché vengono catturati solo i nodi foglia canaglia che hanno un antenato comune con nodi foglia di "livello massimo" (quando "1.2.5.8" non è presente ", 1.2.4" e "1.2.5" sono assenti perché non hanno antenati comuni con nessun nodo foglia di "livello massimo").

Quindi ecco una nuova proposta, mescolando il lavoro di posz con il mio estraendo il NOT EXISTS sottorichiesta e trasformandola in un UNION interno , sfruttando UNION capacità di deduplicazione (sfruttando le capacità di confronto di jsonb):

<!-- language: sql -->
WITH RECURSIVE
c_with_level AS (

    SELECT *, 0 as lvl
    FROM   customer_area_node
    WHERE  parent_id IS NULL

    UNION ALL

    SELECT child.*, parent.lvl + 1
    FROM   customer_area_node child
    JOIN   c_with_level parent ON parent.id = child.parent_id
),
maxlvl AS (
  SELECT max(lvl) maxlvl FROM c_with_level
),
c_tree AS (
    SELECT c_with_level.*, jsonb '[]' children
    FROM   c_with_level, maxlvl
    WHERE  lvl = maxlvl

    UNION 
    (
        SELECT (branch_parent).*, jsonb_agg(branch_child)
        FROM (
            SELECT branch_parent, branch_child
            FROM c_with_level branch_parent
            JOIN c_tree branch_child ON branch_child.parent_id = branch_parent.id
        ) branch
        GROUP BY branch.branch_parent

        UNION

        SELECT c.*, jsonb '[]' children
        FROM   c_with_level c
        WHERE  NOT EXISTS (SELECT 1 FROM c_with_level hypothetical_child WHERE hypothetical_child.parent_id = c.id)
    )
)
SELECT jsonb_pretty(row_to_json(c_tree)::jsonb)
FROM c_tree
WHERE lvl = 0;

Testato su http://rextester.com/SMM38494;)