Toto je klasické použití jednoduchého rekurzivního společného tabulkového výrazu (WITH RECURSIVE
), dostupné v PostgreSQL 8.4 a novějších.
Demonstrováno zde:http://sqlfiddle.com/#!12/78e15/9
Vzhledem k ukázkovým datům jako SQL:
CREATE TABLE Table1
("ID1" text, "ID2" text)
;
INSERT INTO Table1
("ID1", "ID2")
VALUES
('vc1', 'vc2'),
('vc2', 'vc3'),
('vc3', 'vc4'),
('vc4', 'rc7')
;
Můžete napsat:
WITH RECURSIVE chain(from_id, to_id) AS (
SELECT NULL, 'vc2'
UNION
SELECT c.to_id, t."ID2"
FROM chain c
LEFT OUTER JOIN Table1 t ON (t."ID1" = to_id)
WHERE c.to_id IS NOT NULL
)
SELECT from_id FROM chain WHERE to_id IS NULL;
To znamená, že iterativně prochází řetězcem a přidává každý řádek do chain
tabulka jako od-a-ukazatele. Když narazí na řádek, pro který odkaz 'to' neexistuje, přidá pro tento řádek nulový odkaz 'to'. Další iterace si všimne, že odkaz 'to' je nulový a vytvoří nulové řádky, což způsobí ukončení iterace.
Vnější dotaz poté vybere řádky, které byly určeny jako konec řetězce tím, že mají neexistující to_id.
Zorientovat se v rekurzivních CTE vyžaduje trochu úsilí. Klíčové věci, kterým je třeba porozumět, jsou:
-
Začínají výstupem počátečního dotazu, který opakovaně spojují s výstupem „rekurzivní části“ (dotaz za
UNION
neboUNION ALL
), dokud rekurzivní část nepřidá žádné řádky. To zastaví iteraci. -
Ve skutečnosti nejsou rekurzivní, spíše iterativní, i když jsou dobré pro věci, na které byste mohli použít rekurzi.
Takže v podstatě vytváříte stůl ve smyčce. Nemůžete odstraňovat řádky ani je měnit, pouze přidávat nové, takže obecně potřebujete vnější dotaz, který filtruje výsledky, abyste získali požadované řádky výsledků. Často budete přidávat další sloupce obsahující přechodná data, která používáte ke sledování stavu iterace, řízení stop-conditions atd.
Může pomoci podívat se na nefiltrovaný výsledek. Pokud nahradím závěrečný souhrnný dotaz jednoduchým řetězcem SELECT * FROM chain
Vidím tabulku, která byla vygenerována:
from_id | to_id
---------+-------
| vc2
vc2 | vc3
vc3 | vc4
vc4 | rc7
rc7 |
(5 rows)
První řádek je ručně přidaný řádek počátečního bodu, kde zadáte, co chcete vyhledat – v tomto případě to bylo vc2
. Každý následující řádek byl přidán UNION
ed rekurzivní termín, který provede LEFT OUTER JOIN
na předchozí výsledek a vrátí novou sadu řádků, které spárují předchozí to_id
(nyní v from_id
sloupec) na další to_id
. Pokud LEFT OUTER JOIN
neodpovídá potom to_id
bude null, což způsobí, že další vyvolání vrátí nyní řádky a ukončí iteraci.
Protože tento dotaz se nepokouší přidat pouze poslední řádek pokaždé, je to vlastně opakování slušného kusu práce při každé iteraci. Abyste tomu zabránili, museli byste použít přístup podobný Gordonovi, ale navíc při skenování vstupní tabulky filtrovat předchozí pole hloubky, takže jste spojili pouze nejnovější řádek. V praxi to obvykle není nutné, ale může to být problém u velmi velkých souborů dat nebo tam, kde nemůžete vytvořit vhodné indexy.
Více se můžete dozvědět v dokumentaci PostgreSQL na CTE.