Entschuldigen Sie die sehr späte Antwort, aber ich glaube, ich habe eine elegante Lösung gefunden, die eine akzeptierte Antwort auf diese Frage werden könnte.
Basierend auf dem großartigen "kleinen Hack", der von @pozs gefunden wurde, habe ich eine Lösung gefunden, die:
- löst die "Rogue Leaves"-Situation mit sehr wenig Code (unter Nutzung der
NOT EXISTS
Prädikat) - vermeidet das ganze Levelberechnungs-/Bedingungskram
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
Aktualisieren :
Mit sehr einfachen Daten getestet, funktioniert es, aber wie posz in einem Kommentar betonte, werden bei seinen Beispieldaten einige Rogue-Leaf-Knoten vergessen. Aber ich habe herausgefunden, dass bei noch komplexeren Daten die vorherige Antwort auch nicht funktioniert, da nur Rogue-Blattknoten mit einem gemeinsamen Vorfahren mit Blattknoten der "maximalen Ebene" abgefangen werden (wenn "1.2.5.8" nicht vorhanden ist, " 1.2.4" und "1.2.5" fehlen, da sie keinen gemeinsamen Vorfahren mit einem Blattknoten der "maximalen Ebene" haben).
Hier ist also ein neuer Vorschlag, der die Arbeit von posz mit meiner mischt, indem er NOT EXISTS
extrahiert subrequest und macht daraus eine interne UNION
, wobei UNION
genutzt wird Deduplizierungsfähigkeiten (unter Nutzung von jsonb-Vergleichsfähigkeiten):
<!-- 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;
Getestet auf http://rextester.com/SMM38494;)