Rekursive CTEs
Ein rekursiver (selbstreferenzierender) CTE ist eine UNION
, die mindestens ein nicht-rekursives Element namens anchor haben muss.Das/die nicht-rekursive(n) Element(e) muss/müssen vor dem/den rekursiven Element(en) platziert werden.Rekursive Elemente sind miteinander und mit ihrem nicht-rekursiven Nachbarn durch UNION ALL
-Operatoren verknüpft.Die Vereinigungen zwischen nicht-rekursiven Mitgliedern können von jedem Typ sein.
Rekursive CTEs erfordern, dass das Schlüsselwort RECURSIVE
direkt nach WITH
vorhanden ist.Jedes rekursive Unionsmitglied darf nur einmal auf sich selbst verweisen, und zwar in einer FROM
-Klausel.
Ein großer Vorteil rekursiver CTEs besteht darin, dass sie weit weniger Speicher und CPU-Zyklen benötigen als eine entsprechende rekursive gespeicherte Prozedur.
Ausführungsmuster
Das Ausführungsmuster eines rekursiven CTE sieht wie folgt aus:
-
Die Engine beginnt mit der Ausführung von einem nicht-rekursiven Member.
-
Für jede ausgewertete Zeile beginnt es, jedes rekursive Element nacheinander auszuführen, wobei die aktuellen Werte aus der äußeren Zeile als Parameter verwendet werden.
-
Wenn die aktuell ausgeführte Instanz eines rekursiven Members keine Zeilen erzeugt, führt die Ausführung eine Schleife zurück und ruft die nächste Zeile aus der äußeren Ergebnismenge ab.
Beispiel für rekursive CTEs
WITH RECURSIVE DEPT_YEAR_BUDGET AS (
SELECT
FISCAL_YEAR,
DEPT_NO,
SUM(PROJECTED_BUDGET) BUDGET
FROM PROJ_DEPT_BUDGET
GROUP BY FISCAL_YEAR, DEPT_NO
),
DEPT_TREE AS (
SELECT
DEPT_NO,
HEAD_DEPT,
DEPARTMENT,
CAST('' AS VARCHAR(255)) AS INDENT
FROM DEPARTMENT
WHERE HEAD_DEPT IS NULL
UNION ALL
SELECT
D.DEPT_NO,
D.HEAD_DEPT,
D.DEPARTMENT,
H.INDENT || ' '
FROM DEPARTMENT D
JOIN DEPT_TREE H ON H.HEAD_DEPT = D.DEPT_NO
)
SELECT
D.DEPT_NO,
D.INDENT || D.DEPARTMENT DEPARTMENT,
DYB_2008.BUDGET AS BUDGET_08,
DYB_2009.BUDGET AS BUDGET_09
FROM DEPT_TREE D
LEFT JOIN DEPT_YEAR_BUDGET DYB_2008 ON
(D.DEPT_NO = DYB_2008.DEPT_NO) AND
(DYB_2008.FISCAL_YEAR = 2008)
LEFT JOIN DEPT_YEAR_BUDGET DYB_2009 ON
(D.DEPT_NO = DYB_2009.DEPT_NO) AND
(DYB_2009.FISCAL_YEAR = 2009);
Das nächste Beispiel gibt den Stammbaum eines Pferdes zurück.Der Hauptunterschied besteht darin, dass die Rekursion in zwei Zweigen des Stammbaums gleichzeitig auftritt.
WITH RECURSIVE PEDIGREE (
CODE_HORSE,
CODE_FATHER,
CODE_MOTHER,
NAME,
MARK,
DEPTH)
AS (SELECT
HORSE.CODE_HORSE,
HORSE.CODE_FATHER,
HORSE.CODE_MOTHER,
HORSE.NAME,
CAST('' AS VARCHAR(80)),
0
FROM
HORSE
WHERE
HORSE.CODE_HORSE = :CODE_HORSE
UNION ALL
SELECT
HORSE.CODE_HORSE,
HORSE.CODE_FATHER,
HORSE.CODE_MOTHER,
HORSE.NAME,
'F' || PEDIGREE.MARK,
PEDIGREE.DEPTH + 1
FROM
HORSE
JOIN PEDIGREE
ON HORSE.CODE_HORSE = PEDIGREE.CODE_FATHER
WHERE
PEDIGREE.DEPTH < :MAX_DEPTH
UNION ALL
SELECT
HORSE.CODE_HORSE,
HORSE.CODE_FATHER,
HORSE.CODE_MOTHER,
HORSE.NAME,
'M' || PEDIGREE.MARK,
PEDIGREE.DEPTH + 1
FROM
HORSE
JOIN PEDIGREE
ON HORSE.CODE_HORSE = PEDIGREE.CODE_MOTHER
WHERE
PEDIGREE.DEPTH < :MAX_DEPTH
)
SELECT
CODE_HORSE,
NAME,
MARK,
DEPTH
FROM
PEDIGREE
-
Aggregate (
DISTINCT
,GROUP BY
,HAVING
) und Aggregatfunktionen (SUM
,COUNT
,MAX
usw.) sind in rekursiven Unionselementen nicht erlaubt. -
Eine rekursive Referenz kann nicht an einem Outer Join teilnehmen.
-
Die maximale Rekursionstiefe beträgt 1024.