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

Postgres rekursiv forespørgsel med row_to_json

Beklager det meget sene svar, men jeg tror, ​​jeg fandt en elegant løsning, der kunne blive et accepteret svar på dette spørgsmål.

Baseret på det fantastiske "lille hack" fundet af @pozs, kom jeg med en løsning, der:

  • løser situationen "rogue leaves" med meget lidt kode (ved at udnytte NOT EXISTS prædikat)
  • undgår hele niveauberegningen/tilstandstinget
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

Opdater :

Testet med meget simple data virker det, men som posz påpegede i en kommentar, med hans eksempeldata er nogle useriøse bladknuder glemt. Men jeg fandt ud af, at med endnu mere komplekse data, virker det tidligere svar heller ikke, fordi kun useriøse bladknuder, der har en fælles forfader med "max level" bladknuder, fanges (når "1.2.5.8" ikke er der, " 1.2.4" og "1.2.5" er fraværende, fordi de ikke har nogen fælles forfader med nogen "max level" bladknude).

Så her er et nyt forslag, der blander posz's arbejde med mit ved at udtrække NOT EXISTS underanmodning og gør den til en intern UNION , ved at udnytte UNION de-duplikeringsevner (udnytter jsonb-sammenligningsevner):

<!-- 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;

Testet på http://rextester.com/SMM38494;)



  1. Får en mærkelig fejl, SQL Server-forespørgsel ved hjælp af `WITH`-klausul

  2. Hvad er de 6 hovedkomponenter i Microsoft Access?

  3. Forstå log buffer skylninger

  4. Felttyper og anvendelser i Access 2019-databaser