Ein SQL-CTE (Common Table Expression, deutsch: gemeinsamer Tabellenausdruck) ist ein benanntes, temporäres Zwischenergebnis, das mit der WITH-Klausel direkt vor einer Abfrage definiert wird. Das CTE existiert nur für die Dauer einer einzigen Anweisung und lässt sich in der nachfolgenden Abfrage wie eine normale Tabelle verwenden. CTEs machen mehrstufige Abfragen lesbarer, weil jeder Zwischenschritt einen Namen und eine klar abgegrenzte Definition bekommt.

Syntax: WITH und ein SELECT

Die Grundform ist simpel: Nach WITH folgt der Name des CTE, dann AS und in Klammern die definierende Abfrage. Direkt danach kommt das äußere SELECT, das das CTE wie eine Tabelle referenziert:

WITH umsatz_pro_kunde AS (
    SELECT kunden_id, SUM(betrag) AS umsatz
    FROM bestellungen
    GROUP BY kunden_id
)
SELECT k.name, u.umsatz
FROM kunden k
JOIN umsatz_pro_kunde u ON u.kunden_id = k.id;

Mehrere CTEs in einer Anweisung

Mehrere CTEs werden durch Kommas getrennt aufgelistet. Ein späteres CTE darf dabei ein früheres referenzieren — so lassen sich komplexe Berechnungen in kleine, benannte Schritte zerlegen. Im Gegensatz zu einer Subquery kann dasselbe CTE innerhalb der Anweisung auch mehrfach referenziert werden, ohne den Code zu duplizieren.

Rekursive CTEs für Bäume und Sequenzen

Mit WITH RECURSIVE kann sich ein CTE selbst referenzieren. Ein rekursives CTE besteht aus einem Anker-SELECT (Startzeilen) und einem rekursiven SELECT, das mit UNION ALL angehängt wird und so lange weitere Zeilen erzeugt, bis nichts Neues mehr hinzukommt. Damit lassen sich Organigramme, Kommentarbäume oder Zahlenfolgen abbilden:

WITH RECURSIVE zahlen AS (
    SELECT 1 AS n
    UNION ALL
    SELECT n + 1 FROM zahlen WHERE n < 10
)
SELECT n FROM zahlen;

CTE, Subquery oder Sicht?

Eine Subquery löst dieselbe Aufgabe oft ebenfalls — bei mehreren Verwendungen derselben Logik wird der Code mit CTEs aber deutlich klarer. Anders als eine View (Sicht) wird ein CTE nicht dauerhaft gespeichert, sondern existiert nur für die eine Anweisung. Die Datenbank entscheidet selbst, ob sie das CTE materialisiert oder die Definition inline auflöst.

Verfügbarkeit

Die WITH-Klausel beherrschen unter anderem MariaDB (ab 10.2), MySQL (ab 8.0), PostgreSQL, SQL Server (ab 2005) und SQLite (ab 3.8.3). Die Kombination mit GROUP BY und HAVING funktioniert innerhalb eines CTE genauso wie in einer normalen Abfrage. Grundlagen der Sprache liefert der Artikel SQL-Grundlagen.