Sie können an mehreren Stellen vereinfachen (vorausgesetzt acct_id
und parent_id
sind NOT NULL
):
WITH RECURSIVE search_graph AS (
SELECT parent_id, ARRAY[acct_id] AS path
FROM account
UNION ALL
SELECT g.parent_id, sg.path || g.acct_id
FROM search_graph sg
JOIN account g ON g.acct_id = sg.parent_id
WHERE g.acct_id <> ALL(sg.path)
)
SELECT path[1] AS child
, path[array_upper(path,1)] AS parent
, path
FROM search_graph
ORDER BY path;
- Die Spalten
acct_id
,depth
,cycle
sind nur Rauschen in deiner Abfrage. - Das
WHERE
Bedingung muss die Rekursion einen Schritt früher verlassen, vor der doppelte Eintrag vom obersten Knoten ist im Ergebnis. Das war in deinem Original ein "off-by-one".
Der Rest ist Formatierung.
Wenn Sie wissen der einzig mögliche Kreis in Ihrem Diagramm ist eine Selbstreferenz, das können wir billiger haben:
WITH RECURSIVE search_graph AS (
SELECT parent_id, ARRAY[acct_id] AS path, acct_id <> parent_id AS keep_going
FROM account
UNION ALL
SELECT g.parent_id, sg.path || g.acct_id, g.acct_id <> g.parent_id
FROM search_graph sg
JOIN account g ON g.acct_id = sg.parent_id
WHERE sg.keep_going
)
SELECT path[1] AS child
, path[array_upper(path,1)] AS parent
, path
FROM search_graph
ORDER BY path;
SQL-Geige.
Beachten Sie, dass es Probleme geben würde (zumindest bis pg v9.4) für Datentypen mit einem Modifikator (wie varchar(5)
), weil die Array-Verkettung den Modifikator verliert, aber der rCTE darauf besteht, dass die Typen genau übereinstimmen:
- Überraschende Ergebnisse für Datentypen mit Typmodifikator