Можете да опростите на няколко места (приемайки acct_id
и parent_id
са 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;
- Колоните
acct_id
,depth
,cycle
са просто шум във вашата заявка. WHERE
условието трябва да излезе от рекурсията една стъпка по-рано, преди дублираният запис от горния възел е в резултата. Това беше „изключено по едно“ във вашия оригинал.
Останалото е форматиране.
Ако знаете единственият възможен кръг във вашата графика е самореференция, можем да имаме това по-евтино:
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 Fiddle.
Имайте предвид, че ще има проблеми (поне до pg v9.4) за типове данни с модификатор (като varchar(5)
), защото конкатенацията на масив губи модификатора, но rCTE настоява типове да съвпадат точно:
- Изненадващи резултати за типове данни с модификатор на тип