23rd Jun 2022 9 Leseminuten Was ist eine rekursive CTE in SQL? Tihomir Babic CTE Rekursive Abfragen Inhaltsverzeichnis Was sind CTEs? Nicht-rekursive CTE-Syntax Rekursive CTE-Syntax Beispiel 1 - Suche nach Vorgesetzten und Hierarchiestufen für alle Mitarbeiter Beispiel 2 - Ermittlung des Investitionsbetrags nach Investor Beispiel 3 - Suche nach Strecken zwischen Städten Rekursive CTEs weiter üben Der Artikel, der Ihnen praktische Beispiele für die Verwendung rekursiver CTEs in SQL zeigt. Wenn Sie schon von den rekursiven CTEs in SQL gehört haben, sie aber noch nie benutzt haben, ist dieser Artikel genau das Richtige für Sie. Er ist auch etwas für Sie, wenn Sie nie genug von rekursiven CTE-Beispielen bekommen können. Bevor wir uns mit der Rekursion befassen, werde ich Sie daran erinnern, was CTEs sind und was ihre Syntax ist. Dann werde ich das Gleiche für rekursive CTEs tun. Danach zeige ich Ihnen anhand von drei Beispielen, wie rekursive CTEs funktionieren. Was sind CTEs? Die CTE (common table expression), auch bekannt als WITH Klausel, ist eine SQL-Funktion, die einen temporären Datensatz zurückgibt, der von einer anderen Abfrage verwendet werden kann. Da es sich um ein temporäres Ergebnis handelt, wird es nirgendwo gespeichert, aber es kann dennoch wie jede andere Tabelle referenziert werden. Es gibt zwei Arten von CTEs, nicht rekursive und rekursive. Hier ist ein guter Artikel, der Ihnen zeigt, was CTEs sind und wie sie funktionieren. Nicht-rekursive CTE-Syntax Die allgemeine Syntax einer nicht rekursiven CTE sieht wie folgt aus: WITH cte_name AS (cte_query_definition) SELECT * FROM cte_name; Der erste Teil der Syntax ist das CTE. Er beginnt mit dem Schlüsselwort WITH. Dann geben Sie Ihrem CTE einen Namen. Danach folgt das Schlüsselwort AS, und in den Klammern können Sie das CTE definieren. Der zweite Teil der Syntax ist eine einfache SELECT -Anweisung. Sie wird unmittelbar nach dem rekursiven CTE geschrieben, ohne Kommas, Semikolons oder ähnliche Zeichen. Wie ich bereits sagte, wird die CTE in einer anderen Abfrage wie jede andere Tabelle verwendet. Das ist genau das, was die Anweisung SELECT tut. Hier ist der Artikel, der Ihnen zusätzlich mit der CTE-Syntax und ihren Regeln helfen kann. Und wenn Sie weitere CTE-Beispiele benötigen, ist dieser Artikel genau das Richtige für Sie. Rekursive CTE-Syntax Ein rekursiver CTE verweist auf sich selbst. Sie gibt eine Teilmenge der Ergebnisse zurück, verweist dann wiederholt (rekursiv) auf sich selbst und hört auf, wenn sie alle Ergebnisse zurückgibt. Die Syntax für eine rekursive CTE unterscheidet sich nicht allzu sehr von der einer nicht rekursiven CTE: WITH RECURSIVE cte_name AS ( cte_query_definition (the anchor member) UNION ALL cte_query_definition (the recursive member) ) SELECT * FROM cte_name; Auch hier steht am Anfang des CTE die Klausel WITH. Wenn Sie jedoch möchten, dass Ihre CTE rekursiv ist, dann schreiben Sie nach WITH das Schlüsselwort RECURSIVE. Dann geht es weiter wie gewohnt: AS wird von den Klammern mit der CTE-Abfragedefinition gefolgt. Diese erste Abfragedefinition wird als Ankerelement bezeichnet. Um das Ankerelement mit dem rekursiven Element zu verbinden, müssen Sie den Befehl UNION oder UNION ALL verwenden. Das rekursive Mitglied ist natürlich der rekursive Teil von CTE, der auf das CTE selbst verweist. Wie das funktioniert, werden Sie gleich in einem Beispiel sehen. Rekursive CTEs werden vor allem dann verwendet, wenn Sie hierarchische Daten oder Diagramme abfragen möchten. Dabei kann es sich um die Organisationsstruktur eines Unternehmens, einen Familienstammbaum, eine Restaurantkarte oder verschiedene Routen zwischen Städten handeln. In diesen Artikeln erfahren Sie, wie CTEs mit hierarchischen Strukturen arbeiten und wie man Graphdaten abfragt. Nachdem wir nun verstanden haben, wie rekursive CTEs funktionieren, wollen wir uns einige Beispiele ansehen. Beispiel 1 - Suche nach Vorgesetzten und Hierarchiestufen für alle Mitarbeiter Für dieses Problem verwende ich Daten aus der Tabelle employeesdie die folgenden Spalten enthält: id: Die ID des Mitarbeiters. first_name: Der Vorname des Mitarbeiters. last_name: Nachname des Mitarbeiters. boss_id: Die ID des Vorgesetzten des Mitarbeiters. So sehen die Daten aus: idfirst_namelast_nameboss_id 1DomenicLeaver5 2ClevelandHewins1 3KakalinaAtherton8 4RoxannaFairlieNULL 5HermieComsty4 6PoohGoss8 7FaulknerChalliss5 8BobbeBlakeway4 9LaureneBurchill1 10AugustaGosdin8 Es ist nicht allzu kompliziert. Zum Beispiel ist der Chef von Domenic Leaver der Mitarbeiter mit der ID 5; das ist Hermie Comsty. Das gleiche Prinzip gilt für alle anderen Mitarbeiter, außer für Roxanna Fairlie. Sie hat keinen Chef; in der Spalte boss_id steht der Wert NULL. Daraus können wir schließen, dass Roxanna der Präsident oder Eigentümer der Firma ist. Schreiben wir nun die rekursive CTE, um alle Mitarbeiter und ihre direkten Vorgesetzten aufzulisten. WITH RECURSIVE company_hierarchy AS ( SELECT id, first_name, last_name, boss_id, 0 AS hierarchy_level FROM employees WHERE boss_id IS NULL UNION ALL SELECT e.id, e.first_name, e.last_name, e.boss_id, hierarchy_level + 1 FROM employees e, company_hierarchy ch WHERE e.boss_id = ch.id ) SELECT ch.first_name AS employee_first_name, ch.last_name AS employee_last_name, e.first_name AS boss_first_name, e.last_name AS boss_last_name, hierarchy_level FROM company_hierarchy ch LEFT JOIN employees e ON ch.boss_id = e.id ORDER BY ch.hierarchy_level, ch.boss_id; Was macht diese Abfrage? Es ist eine rekursive Abfrage, also beginnt sie mit WITH RECURSIVE. Der Name der CTE ist company_hierarchy. Nach AS steht die CTE-Definition in Klammern. Die erste Anweisung SELECT wählt alle employee Tabellenspalten aus, in denen die Spalte boss_id den Wert NULL hat. Kurz gesagt, sie wählt Roxanna Fairlie aus, weil nur sie den Wert NULL in dieser Spalte hat. Noch kürzer: Ich beginne die Rekursion an der Spitze der Organisationsstruktur. Es gibt auch eine Spalte hierarchy_level mit dem Wert 0. Das bedeutet, dass die Ebene des Eigentümers/Präsidenten 0 ist - er befindet sich an der Spitze der Hierarchie. Ich habe die UNION ALL verwendet, um diese SELECT-Anweisung mit der zweiten zu verbinden, d. h. mit dem rekursiven Mitglied. Im rekursiven Mitglied wähle ich alle Spalten aus der Tabelle employees und die CTE company_hierarchy, wobei die Spalte boss_id gleich der Spalte id ist. Beachten Sie den Teilhierarchy_level + 1. Das bedeutet, dass die CTE bei jeder Rekursion 1 zur vorherigen Hierarchieebene hinzufügt, und zwar so lange, bis sie das Ende der Hierarchie erreicht. Beachten Sie auch, dass ich diese CTE wie jede andere Tabelle behandle. Um die Definition der CTE zu beenden, schließen Sie einfach die Klammern. Schließlich gibt es noch eine dritte Anweisung SELECT, die sich außerhalb der CTE befindet. Sie wählt die Spalten aus, in denen die Mitarbeiter, die Namen ihrer Vorgesetzten und die Hierarchieebene angezeigt werden sollen. Die Daten werden aus der CTE und der Tabelle employees. Ich habe diese beiden mit LEFT JOIN verknüpft, da ich alle Daten aus der CTE haben möchte - einschließlich Roxanna Fairlie, die den Wert NULL in der Spalte boss_id hat. Das Ergebnis wird in aufsteigender Reihenfolge angezeigt: zuerst nach der Hierarchieebene, dann nach der ID des Vorgesetzten. So sieht es aus: employee_first_nameemployee_last_nameboss_first_nameboss_last_namehierarchy_level RoxannaFairlieNULLNULL0 HermieComstyRoxannaFairlie1 BobbeBlakewayRoxannaFairlie1 DomenicLeaverHermieComsty2 FaulknerChallissHermieComsty2 AugustaGosdinBobbeBlakeway2 PoohGossBobbeBlakeway2 KakalinaAthertonBobbeBlakeway2 LaureneBurchillDomenicLeaver3 ClevelandHewinsDomenicLeaver3 Roxanna Fairlie ist die oberste Chefin; das wussten Sie bereits. Es gibt zwei Mitarbeiter auf Ebene 1. Das heißt, Bobbe Blakeway und Hermie Comsty sind Roxanna Fairlie direkt unterstellt. Auf Ebene 2 gibt es Angestellte, deren direkte Vorgesetzte Bobbe Blakeway und Hermie Comsty sind. Es gibt auch eine dritte Ebene in der Hierarchie. Das sind die Mitarbeiter, deren direkter Vorgesetzter Domenic Leaver ist. Beispiel 2 - Ermittlung des Investitionsbetrags nach Investor In diesem Beispiel verwende ich die Tabelle investment: id: Die ID der Investition. investment_amount: Der Betrag der Investition. Die Daten in der Tabelle sehen wie folgt aus: idinvestment_amount 19,705,321.00 25,612,948.60 35,322,146.00 Dies sind die Beträge der drei möglichen Investitionsoptionen. Sie werden von den drei Investoren berücksichtigt, die den gesamten Investitionsbetrag in gleiche Teile aufteilen werden. Ihre Aufgabe ist es, den Betrag pro Investor in Abhängigkeit von der Anzahl der Investoren zu berechnen, d. h. ob ein, zwei, drei oder kein Investor in jede Anlage investiert. Die Abfrage, mit der dieses Problem gelöst wird, lautet: WITH RECURSIVE per_investor_amount AS ( SELECT 0 AS investors_number, 0.00 AS investment_amount, 0.00 AS individual_amount UNION SELECT investors_number + 1, i.investment_amount, i.investment_amount / (investors_number + 1) FROM investment i, per_investor_amount pia WHERE investors_number << 3 ) SELECT * FROM per_investor_amount ORDER BY investment_amount, investors_number; Auch hier beginnt die CTE mit WITH RECURSIVE, gefolgt von ihrem Namen und der Abfragedefinition. Dieses Mal verwende ich das Ankerelement der rekursiven Abfrage, um einige Daten zu erstellen. Die Spalten sind investors_number, investment_amount und individual_amount. Dies ist der Punkt, an dem die Rekursion beginnen soll (genauso wie im vorherigen Beispiel mit hierarchy_level = 0). Dann kommen UNION und das rekursive Mitglied. Dieser Teil der Abfrage wird die Spalte investors_number bei jeder Rekursion um eins erhöhen. Dies geschieht für jede investment_amount. In der dritten Spalte wird der Betrag dieser Investition pro Anleger berechnet, abhängig von der Anzahl der beteiligten Anleger. Die Rekursion wird für bis zu drei Anleger durchgeführt (d. h. bis die Bedingung WHERE investors_number < 3 erreicht ist). Danach folgt die einfache SELECT-Anweisung, die alle Spalten aus dem CTE zurückgibt. Und hier ist das Ergebnis: investors_numberinvestment_amountindividual_amount 00.000.00 15,322,146.005,322,146.00 25,322,146.002,661,073.00 35,322,146.001,774,048.67 15,612,948.605,612,948.60 25,612,948.602,806,474.30 35,612,948.601,870,982.87 19,705,321.009,705,321.00 29,705,321.004,852,660.50 39,705,321.003,235,107.00 Es ist nicht schwer, es zu analysieren. Wenn es keine Investoren gibt, ist der Investitionsbetrag gleich Null, ebenso wie der individuelle Betrag. Wenn die Investition 5.322.146,00 beträgt und es nur einen Anleger gibt, dann ist der Betrag pro Anleger 5.322.146,00. Wenn zwei Anleger den gleichen Betrag investieren, muss jeder von ihnen 2.661.073,00 zahlen. Wenn sich alle drei Anleger für eine Investition entscheiden, zahlt jeder von ihnen 1.774.048,67. Die beiden anderen Investitionsbeträge folgen demselben Muster, wie Sie in der Tabelle sehen können. Beispiel 3 - Suche nach Strecken zwischen Städten Im dritten Beispiel verwende ich die Tabelle cities_route, die Daten über niederländische Städte enthält: city_from: Die Abfahrtsstadt. city_to: Die Zielstadt. distance: Die Entfernung zwischen zwei Städten, in Kilometern. city_fromcity_todistance GroningenHeerenveen61.4 GroningenHarlingen91.6 HarlingenWieringerwerf52.3 WieringerwerfHoorn26.5 HoornAmsterdam46.1 AmsterdamHaarlem30 HeerenveenLelystad74 LelystadAmsterdam57.2 Verwenden Sie diese Tabelle, um alle möglichen Routen von Groningen nach Haarlem zu finden, mit Angabe der Städte auf der Route und der Gesamtentfernung. Hier ist die Abfrage zur Lösung dieses Problems: WITH RECURSIVE possible_route AS ( SELECT cr.city_to, cr.city_from || '->' ||cr.city_to AS route, cr.distance FROM cities_route cr WHERE cr.city_from = 'Groningen' UNION ALL SELECT cr.city_to, pr.route || '->' || cr.city_to AS route, CAST((pr.distance + cr.distance) AS DECIMAL(10, 2)) FROM possible_route pr INNER JOIN cities_route cr ON cr.city_from = pr.city_to ) SELECT pr.route, pr.distance FROM possible_route pr WHERE pr.city_to = 'Haarlem' ORDER BY pr.distance; Schauen wir uns an, was diese Abfrage macht. Die erste Anweisung SELECT in der CTE-Definition wählt die Spalten aus der Tabelle cities_route aus, wobei die Ausgangsstadt Groningen ist. Beachten Sie, dass es auch eine neue Spalte mit dem Namen route gibt, die ich verwenden werde, um die Städte auf der Route zu verketten. Die UNION ALL verbindet dies mit dem rekursiven Mitglied. Diese SELECT Anweisung wählt die Ankunftsstadt aus, verkettet die Städte auf der Route und addiert schließlich die Entfernungen zwischen diesen Städten zur Gesamtsumme der Route zwischen Groningen und Haarlem. Um all dies zu erreichen, habe ich die CTE mit der Tabelle cities_route. Dann kommt die Anweisung SELECT, die Daten aus der CTE abruft. Sie wählt die Strecke und die Entfernung aus, wenn die Ankunftsstadt Haarlem ist, wobei die Daten nach der Entfernung in aufsteigender Reihenfolge geordnet sind. Das Abfrageergebnis sieht wie folgt aus: routedistance Groningen->Heerenveen->Lelystad->Amsterdam->Haarlem222.6 Groningen->Harlingen->Wieringerwerf->Hoorn->Amsterdam->Haarlem246.5 Es ist nicht schwer, diese Tabelle zu verstehen. Es gibt zwei Strecken von Groningen nach Haarlem. Sie beinhalten verschiedene Städte dazwischen und sind 222,6 km bzw. 246,5 km lang. Wenn Sie weiter lernen wollen, sehen Sie sich an, wie Sie eine rekursive CTE anstelle einer langen SQL-Abfrage verwenden können. Und nachdem Sie sich mit diesem Thema beschäftigt haben, machen Sie sich einen Spaß daraus, etwas mit einer rekursiven CTE zu zeichnen. Rekursive CTEs weiter üben Diese drei Beispiele haben die Möglichkeiten von rekursiven CTEs in SQL gezeigt. Jetzt ist es an der Zeit, das Gelernte zu vertiefen. Am besten probieren Sie es mit unserem KursRekursive Abfragen . Er bietet Ihnen eine Fülle von Beispielen, Erklärungen und Übungsmöglichkeiten. Der Kurs ist Teil der Kursreihe Fortgeschrittenes SQL , in der Sie weitere fortgeschrittene SQL-Themen wie Fensterfunktionen, GROUP BY-Erweiterungen und rekursive Abfragen kennenlernen können. Viel Spaß! Tags: CTE Rekursive Abfragen