Literate programming ist eine von Donald Knuth propagierte Programmiermethode, die – unter anderem – danach strebt, den Quelltext so zu gestalten, dass er wie ein Roman von vorne nach hinten gelesen werden kann.
SQL funktioniert genau umgekehrt: Die Verschachtelung von Unterabfragen führt dazu, dass man zuerst die innerste Abfrage suchen und verstehen muss, um sich von dort aus nach außen arbeiten zu können. Nicht einmal gut gewählte Namen können diesen rekursiven Leseprozess merklich verbessern, weil die Namen den Unterabfragen folgen.
Sogar die Reihenfolge der Klauseln der einfachsten SQL-Abfrage hat eine Vorwärtsabhängikeit: Die Bedeutung der select
-Klausel hängt von der from
-Klausel ab. Das stellt Entwicklungswerkzeuge sogar vor eine unlösbare Aufgabe: Wenn man select
eingibt, ist noch nicht bekannt, auf welche Tabellen zugegriffen wird und daher auch nicht, welche Spalten sichtbar sind. Ohne from
-Klausel keine sinnvollen Vorschläge für die select
-Klausel.
Mit SQL:1999 wurde zumindest zur Verschachtelung eine Alternative geschaffen: die with
-Klausel. Im Zusammenhang mit literarischem SQL hat die with
-Klausel zwei wichtige Eigenschaften: (1) Namen kommen zuerst; (2) Unterabfragen können beliebig angeordnet werden.
Namen zuerst
Die Wichtigkeit aussagekräftiger Namen für Softwarekomponenten kann nicht genug betont werden. Aber selbst der beste Name, der die Absicht der Komponente verrät, ist nutzlos, wenn er im entscheidenden Moment nicht ins Auge springt:
SELECT ...
FROM (SELECT ...
FROM ...
) intention_revealing_name
...
Je länger die innere Abfrage wird, desto wichtiger ist ein guter Name, aber desto weiter wird er auch aus dem Blickfeld geschoben. Wie tragisch.
Bei der with
-Klausel steht der Name jedoch vor dem Code, so wie bei Funktionsdeklarationen in praktisch jeder anderen Programmiersprache:
WITH intention_revealing_name AS (
SELECT ...
FROM ...
)
SELECT ...
FROM intention_revealing_name irn
...
Als kleines Extra kann man in der from
-Klausel noch immer einen kurzen Alias vergeben (irn
im Beispiel).
Hinweis in eigener Sache
Ich biete SQL Schulungen, Optimierung und Beratung an. Auch der Kauf meines Buches „SQL Performance Explained“ (ab €9,95) unterstützt meine Arbeit an dieser Webseite.
Reihenfolge der menschlichen Logik
Der zweite Aspekt der with
-Klausel ist, dass man damit Verschachtelungen in Verkettungen verwandeln kann. Dadurch werden komplexe Abfragen von oben nach unten lesbar.
Eine with
-Klausel kann mehrere Abfragen beinhalten. Jede davon kann sich bereits auf zuvor definiert Abfragen aus derselben with
-Klausel beziehen. Das ist genau, was man für literarisches SQL braucht.
Das folgende Beispiel skizziert eine literarische Abfrage. Die ersten beiden Definitionen werden nicht einmal gezeigt – die Namen alleine verraten den Zweck:
WITH frequently_bought_together (ordered, suggested, times) AS (
SELECT ...
)
, frequently_bought_shortly_after (ordered, suggested, times) AS (
SELECT ...
)
, product_recommendation (ordered, suggested, times) AS (
SELECT ordered, suggested, SUM(times)
FROM (SELECT * FROM frequently_bought_together
UNION ALL
SELECT * FROM frequently_bought_shortly_after
) frequently_needed_together
GROUP BY ordered, suggested
ORDER BY SUM(times) DESC
FETCH FIRST 1 ROW ONLY
)
SELECT *
FROM product_recommendation
Die Leichtigkeit des Lesens ergibt sich nicht automatisch aus der Von-oben-nach-unten-Struktur. Mit sinnlosen Namen wie x
, y
, und z
wäre die Abfrage noch immer schwer zu verstehen. Erst das Zusammenspiel aus guten Namen und guter Struktur bringt den literarischen Durchbruch.
Wie so oft kann man es natürlich auch mit der Aufteilung in with
-Abfragen übertreiben. Kleine Unterabfragen wie frequently_needed_together
im Beispiel müssen die Leserbarkeit nicht unbedingt stören. Wichtig ist eine Aufteilung in überschaubare, logisch zusammengehörende Einheiten.
Andererseits macht es manchmal Sinn, in einer with
-Abfrage selbst wiederum eine with
-Klausel zu verwenden. Man ist nicht auf eine globale Spaghetti-with
-Klausel beschränkt: Syntaktisch gesehen ist with
ein Präfix für select
. Nutze es dort, wo es die Lesbarkeit am besten unterstützt.
Spätestes jetzt sollte es klar sein, warum diese Technik „literarisches SQL“ heißt: Lesbare Abfragen zu schreiben ist eine Kunst, keine Wissenschaft.
Performanceaspekte
Bei manchen Datenbanken – insbesondere PostgreSQL – kann die Verwendung der with
-Klausel die Performance beeinflussen. Siehe „WITH-Klausel: Performance Auswirkungen“.
Kompatibilität
- Es scheint, als können CTEs in Unterabfragen globale CTEs nicht sehen