Kurz gesagt
- Ein Subselect ist eine Abfrage in einer Abfrage: Die innere SELECT-Anweisung steht in Klammern, ihr Ergebnis wird in die äußere Anweisung eingesetzt.
- Skalare Unterabfragen liefern genau einen Wert, mehrzeilige eine Liste (für IN, ANY, ALL), Unterabfragen im FROM eine ganze Tabelle.
- Eine korrelierte Unterabfrage verweist auf eine Spalte der äußeren Abfrage und wird deshalb für jede äußere Zeile neu ausgewertet.
- Der häufigste Stolperstein ist NOT IN mit NULL-Werten in der Unterabfrage: NOT EXISTS ist hier die sichere Variante.
- Viele Subselects lassen sich als JOIN schreiben, meist ohne Leistungsunterschied. Entscheiden Sie nach Lesbarkeit.
Inhaltsverzeichnis
Was ein Subselect genau ist
Microsoft beschreibt die Unterabfrage in der SQL-Server-Dokumentation als Abfrage, die in einer SELECT-, INSERT-, UPDATE- oder DELETE-Anweisung oder in einer weiteren Unterabfrage geschachtelt ist. Die Begriffe Subselect, Subquery, Unterabfrage und geschachtelte Abfrage meinen dasselbe. Welchen Sie verwenden, hängt meist nur davon ab, mit welcher Datenbank-Dokumentation Sie arbeiten.
Das Prinzip ist einfach: Die Datenbank wertet zuerst die innere Abfrage aus und setzt das Ergebnis an der Stelle ein, an der die Klammer steht. Die Unterrichtsmaterialien der WSF bringen es auf den Punkt: Die Unterabfrage ersetzt in der Hauptabfrage das Ergebnis, das sie selbst liefert. Wenn Sie eine Unterabfrage also isoliert ausführen und deren Ergebnis von Hand in die äußere Abfrage tippen würden, käme dasselbe heraus. Diese Vorstellung hilft enorm beim Lesen fremder Abfragen.
Unterabfragen sind übrigens kein Randthema. Laut MySQL-Referenzhandbuch war gerade diese Möglichkeit, Abfragen ineinander zu schachteln, der Anstoß, das frühe SQL "Structured Query Language" zu nennen.
Die Grundsyntax an einem Beispiel
Für alle Beispiele in diesem Artikel nutzen wir zwei kleine Tabellen:
| Tabelle | Spalten |
| kunden | kunden_id, name, stadt |
| bestellungen | bestell_id, kunden_id, betrag, datum |
Die Frage: Welche Bestellungen liegen über dem durchschnittlichen Bestellwert? Ohne Unterabfrage müssten Sie den Durchschnitt erst in einer eigenen Abfrage berechnen und die Zahl dann abschreiben. Mit Subselect geht es in einem Schritt:
SELECT bestell_id, kunden_id, betrag
FROM bestellungen
WHERE betrag > (SELECT AVG(betrag) FROM bestellungen);
Drei Regeln stecken in diesem kleinen Beispiel:
- Die innere Abfrage steht immer in runden Klammern.
- Sie liefert hier genau einen Wert, deshalb funktioniert der Vergleich mit >.
- Sie ist unabhängig: Sie können SELECT AVG(betrag) FROM bestellungen allein ausführen und erhalten eine Zahl.
In SQL Server darf eine Unterabfrage laut
Microsoft Learn überall stehen, wo ein Ausdruck zulässig ist, außerdem in WHERE und HAVING. In der Praxis sind das vier Orte: WHERE, HAVING, die Spaltenliste hinter SELECT und die FROM-Klausel.
Die drei Formen: skalar, mehrzeilig, als Tabelle
Welche Form Sie brauchen, hängt davon ab, was die innere Abfrage zurückgibt. Daraus ergibt sich auch, welche Operatoren erlaubt sind.
| Form | Liefert | Typischer Ort | Passende Operatoren |
| Skalar | genau einen Wert (1 Zeile, 1 Spalte) | WHERE, HAVING, SELECT-Liste | =, <>, <, >, <=, >= |
| Mehrzeilig | eine Spalte mit mehreren Zeilen | WHERE, HAVING | IN, NOT IN, ANY, ALL |
| Tabellenwertig | mehrere Zeilen und Spalten | FROM (abgeleitete Tabelle) | wird wie eine Tabelle abgefragt |
| Existenzprüfung | nur "gibt es Zeilen oder nicht" | WHERE | EXISTS, NOT EXISTS |
Skalare Unterabfrage
Das Durchschnittsbeispiel oben ist skalar. Gefährlich wird es, wenn Sie glauben, ein Wert komme zurück, die Daten aber mehrere liefern. Microsoft hält fest, dass eine Unterabfrage hinter einem einfachen Vergleichsoperator einen einzelnen Wert zurückgeben muss. Sonst bricht die Abfrage mit einem Fehler ab. Aggregatfunktionen wie AVG, MAX oder COUNT ohne GROUP BY sind deshalb die sichersten Kandidaten für skalare Subselects.
Mehrzeilige Unterabfrage mit IN, ANY und ALL
Sie wollen alle Kunden, die schon einmal etwas bestellt haben:
SELECT name, stadt
FROM kunden
WHERE kunden_id IN (SELECT kunden_id FROM bestellungen);
Die innere Abfrage liefert eine Liste von IDs, IN prüft, ob die ID des Kunden darin vorkommt. Mit > ALL (...) prüfen Sie, ob ein Wert größer ist als jeder Wert der Liste, mit > ANY (...), ob er größer ist als mindestens einer.
Unterabfrage im FROM: die abgeleitete Tabelle
Steht das Subselect hinter FROM, behandelt die Datenbank sein Ergebnis wie eine temporäre Tabelle. Das ist praktisch, wenn Sie mit berechneten Spalten weiterrechnen wollen:
SELECT t.kunden_id, t.umsatz, t.anzahl,
t.umsatz / t.anzahl AS schnitt
FROM (
SELECT kunden_id, SUM(betrag) AS umsatz, COUNT(*) AS anzahl
FROM bestellungen
GROUP BY kunden_id
) AS t
WHERE t.umsatz > 1000;
Geben Sie einer abgeleiteten Tabelle immer einen Alias (hier t). Mehrere Datenbanksysteme verlangen ihn zwingend, und mit Alias bleibt die äußere Abfrage lesbar, weil klar ist, woher jede Spalte kommt.
Korrelierte Unterabfrage: wenn die innere Abfrage nach außen schaut
Bisher waren alle Subselects unabhängig. Eine korrelierte Unterabfrage dagegen greift auf eine Spalte der äußeren Abfrage zu. Das MySQL-Handbuch zu korrelierten Unterabfragen definiert sie als Unterabfrage, die auf eine Tabelle verweist, die auch in der äußeren Abfrage vorkommt. Microsoft formuliert es in den SELECT-Beispielen für Transact-SQL so: Eine korrelierte Unterabfrage ist von der äußeren Abfrage abhängig und lässt sich nicht unabhängig von ihr auswerten.
Ein Beispiel macht den Unterschied greifbar. Gesucht sind Bestellungen, die über dem Durchschnitt des jeweiligen Kunden liegen, nicht über dem Gesamtdurchschnitt:
SELECT b.bestell_id, b.kunden_id, b.betrag
FROM bestellungen AS b
WHERE b.betrag > (
SELECT AVG(b2.betrag)
FROM bestellungen AS b2
WHERE b2.kunden_id = b.kunden_id
);
So läuft die Abfrage gedanklich ab:
- Die Datenbank nimmt die erste Zeile der äußeren Abfrage, etwa eine Bestellung von Kunde 7.
- Sie setzt b.kunden_id = 7 in die innere Abfrage ein und berechnet den Durchschnitt aller Bestellungen von Kunde 7.
- Sie vergleicht den Betrag der aktuellen Bestellung mit diesem Durchschnitt und behält die Zeile oder verwirft sie.
- Dann folgt die nächste Zeile, mit einem neuen Kunden und einem neuen Durchschnitt.
Microsoft beschreibt genau dieses Vorgehen: Für jede Zeile der äußeren Tabelle wird ihr Wert in die innere Abfrage eingesetzt, bevor über die Aufnahme ins Ergebnis entschieden wird. Moderne Optimierer schreiben solche Abfragen intern oft um, das Ergebnis entspricht aber immer dieser zeilenweisen Logik.
So erkennen Sie eine korrelierte Unterabfrage
- Innerhalb der Klammer taucht ein Alias oder Tabellenname auf, der nur außen definiert ist (hier b).
- Sie können die innere Abfrage nicht allein ausführen, ohne einen Fehler wie "unbekannte Spalte" zu bekommen.
- Nutzt dieselbe Tabelle innen und außen, sind zwei verschiedene Aliase (b und b2) Pflicht. Ohne sie weiß weder die Datenbank noch der Leser, welche Zeile gemeint ist.
EXISTS und NOT EXISTS: prüfen, ob es passende Zeilen gibt
EXISTS ist fast immer korreliert. Es interessiert sich nicht für die Werte der Unterabfrage, sondern nur dafür, ob sie mindestens eine Zeile liefert. Die PostgreSQL-Dokumentation zu Unterabfrage-Ausdrücken erklärt, dass die Unterabfrage dabei in der Regel nur so weit ausgeführt wird, bis feststeht, ob mindestens eine Zeile zurückkommt.
Kunden ohne jede Bestellung finden Sie so:
SELECT k.name
FROM kunden AS k
WHERE NOT EXISTS (
SELECT 1
FROM bestellungen AS b
WHERE b.kunden_id = k.kunden_id
);
Das SELECT 1 ist eine Konvention: Was in der Spaltenliste steht, spielt für EXISTS keine Rolle. Diese Form ist der robuste Ersatz für NOT IN, dazu gleich mehr bei den Fehlern.
Subselects in der SELECT-Liste
Ein skalares Subselect kann auch eine Spalte erzeugen. Für jeden Kunden die Zahl seiner Bestellungen:
SELECT k.name,
(SELECT COUNT(*)
FROM bestellungen AS b
WHERE b.kunden_id = k.kunden_id) AS anzahl_bestellungen
FROM kunden AS k;
Das ist lesbar und für kleine Berichte völlig in Ordnung. Bei großen Tabellen und mehreren solchen Spalten nebeneinander lohnt ein Blick auf die Variante mit JOIN und GROUP BY, weil jede Spalten-Unterabfrage eine eigene korrelierte Auswertung bedeutet. Kunden ohne Bestellung erhalten hier übrigens 0, weil COUNT auf eine leere Menge 0 zurückgibt.
Unterabfragen in INSERT, UPDATE und DELETE
Subselects sind nicht nur zum Lesen da. Drei typische Muster:
-- Archiv befüllen
INSERT INTO bestellungen_archiv (bestell_id, kunden_id, betrag, datum)
SELECT bestell_id, kunden_id, betrag, datum
FROM bestellungen
WHERE datum < '2024-01-01';
-- 10 % Rabatt auf alle Bestellungen von Kunden aus Köln
UPDATE bestellungen
SET betrag = betrag * 0.9
WHERE kunden_id IN (
SELECT kunden_id FROM kunden WHERE stadt = 'Köln'
);
-- Bestellungen gelöschter Kunden entfernen
DELETE FROM bestellungen
WHERE kunden_id NOT IN (
SELECT kunden_id FROM kunden WHERE kunden_id IS NOT NULL
);
Führen Sie vor jedem UPDATE oder DELETE mit Subselect zuerst die innere Abfrage allein aus und prüfen Sie, welche IDs sie liefert. Wer die Grundlagen noch festigen will, kann Datensätze ändern mit UPDATE und Datensätze löschen mit DELETE direkt im Browser üben, bevor es an echte Daten geht.
Typische Fehler und wie Sie sie vermeiden
- Mehr als ein Wert hinter =. Hinter einem einfachen Vergleichsoperator muss die Unterabfrage genau einen Wert liefern. Laut Microsoft Learn sind GROUP BY und HAVING dort nur erlaubt, wenn weiterhin ein einzelner Wert herauskommt. Kommen mehrere Zeilen zurück, ersetzen Sie = durch IN oder grenzen die Unterabfrage weiter ein.
- NOT IN mit NULL-Werten. Enthält die Liste der Unterabfrage auch nur einen NULL-Wert, liefert NOT IN für keine Zeile "wahr". Die PostgreSQL-Dokumentation beschreibt, dass das Ergebnis dann NULL statt wahr ist, und die Abfrage gibt still null Zeilen zurück. Nutzen Sie NOT EXISTS oder filtern Sie mit WHERE spalte IS NOT NULL. Wie sich fehlende Werte verhalten, lässt sich gut am Thema NULL-Werte in SQL trainieren.
- ORDER BY in der Unterabfrage. In SQL Server ist ORDER BY in einer Unterabfrage nur zusammen mit TOP zulässig. Sortieren Sie in der äußeren Abfrage, dort hat die Reihenfolge eine Wirkung.
- Aggregat direkt in WHERE. WHERE betrag > AVG(betrag) funktioniert nicht, weil WHERE zeilenweise vor jeder Aggregatberechnung arbeitet. Genau dafür gibt es das skalare Subselect oder HAVING.
- Fehlender Alias. Ohne Alias bei abgeleiteten Tabellen und ohne getrennte Aliase bei korrelierten Unterabfragen auf dieselbe Tabelle entstehen Fehler oder, schlimmer, falsche Bezüge.
- Zu tiefe Schachtelung. SQL Server erlaubt laut Microsoft bis zu 32 Ebenen, abhängig von Arbeitsspeicher und Komplexität der Abfrage. Lesbar ist schon die dritte Ebene kaum noch. Ab zwei Ebenen lohnt ein Umbau in eine abgeleitete Tabelle oder einen JOIN.
Unterabfrage oder JOIN?
Diese Frage stellt sich bei fast jeder Abfrage über zwei Tabellen. Die gute Nachricht von Microsoft: Viele Unterabfragen lassen sich auch als JOIN formulieren, und normalerweise gibt es keinen Leistungsunterschied. Nur bei manchen Prüfungen auf das Vorhandensein von Daten erzielt ein JOIN bessere Leistung. Das MySQL-Handbuch hebt hervor, dass Unterabfragen Operationen ermöglichen, die sonst komplexe Joins oder Unions verlangen würden, und dass viele sie als lesbarer empfinden.
Das Beispiel "Kunden ohne Bestellung" als JOIN:
SELECT k.name
FROM kunden AS k
LEFT JOIN bestellungen AS b ON b.kunden_id = k.kunden_id
WHERE b.bestell_id IS NULL;
| Situation | Unsere Empfehlung |
| Vergleich mit einem berechneten Wert (Durchschnitt, Maximum) | skalares Subselect |
| Spalten aus beiden Tabellen im Ergebnis | JOIN |
| Nur prüfen, ob passende Zeilen existieren | EXISTS oder JOIN, nach Lesbarkeit |
| Ausschluss ("hat nie bestellt") | NOT EXISTS oder LEFT JOIN mit IS NULL |
| Weiterrechnen mit Gruppenergebnissen | Subselect im FROM |
Wenn Sie beide Varianten nebeneinander schreiben können, sind Sie flexibel. Der
LEFT JOIN zum Üben ist dafür ein guter nächster Schritt, und wer Ergebnisse untereinander statt nebeneinander braucht, findet bei
SQL UNION die passende Technik.
Warum Unterabfragen durch Tippen sitzen
> Wir sehen bei Lernenden immer wieder dasselbe Muster: Die Definition eines Subselects ist nach fünf Minuten verstanden, die erste eigene korrelierte Unterabfrage scheitert trotzdem. Der Grund ist selten fehlendes Wissen. Es fehlt die Übung im Zerlegen einer Frage in einen inneren und einen äußeren Teil.
>
> Deshalb setzen wir auf aktives Üben statt Zuschauen. Bei uns schreiben Sie die Abfrage selbst, bekommen sofort Rückmeldung und eine verständliche Erklärung zur Lösung. Echte Eingaben statt Multiple Choice, direkt im Browser, ohne Installation und ohne Anmeldung.
>
> Unser Rat für Subselects: Schreiben Sie immer zuerst die innere Abfrage, führen Sie sie aus, prüfen Sie das Ergebnis, und bauen Sie erst dann die äußere Abfrage darum. Wer das ein paar Dutzend Mal getan hat, liest auch verschachtelte Abfragen von Kollegen ohne Mühe.
>
> Thomas Houza
SQL und Excel kostenlos im Browser üben
Auf LogicExcel üben Sie SQL-Befehle und Excel-Formeln in kurzen, interaktiven Aufgaben mit sofortigem Feedback. Sie brauchen kein Konto und keine Software, Ihr Fortschritt wird lokal gespeichert oder auf Wunsch über einen optionalen Account. Starten Sie mit den Grundlagen wie dem NOT-Operator oder DISTINCT für eindeutige Zeilen und arbeiten Sie sich bis zu kombinierten Abfragen vor.
Wer Daten nicht nur abfragt, sondern danach in Berichten weiterverarbeitet, findet in unseren kostenlosen Excel-Übungen die passende Ergänzung. Alles Weitere finden Sie auf der LogicExcel-Startseite.
Häufige Fragen
Ist Subselect dasselbe wie eine Unterabfrage?
Ja. Subselect, Subquery, Unterabfrage und geschachtelte Abfrage bezeichnen dieselbe Technik: eine SELECT-Anweisung in Klammern innerhalb einer anderen Anweisung. Manche Dokumentationen verwenden Subselect etwas enger für die Unterauswahl selbst, im Alltag sind die Begriffe austauschbar.
Was ist der Unterschied zwischen einer normalen und einer korrelierten Unterabfrage?
Eine normale Unterabfrage ist unabhängig und lässt sich allein ausführen, ihr Ergebnis gilt für alle Zeilen der äußeren Abfrage. Eine korrelierte Unterabfrage verweist auf eine Spalte der äußeren Abfrage und wird gedanklich für jede äußere Zeile neu ausgewertet. Sie erkennen sie an einem Alias der äußeren Tabelle innerhalb der Klammer.
Sind Unterabfragen langsamer als JOINs?
Nicht grundsätzlich. Laut Microsoft gibt es zwischen einer Unterabfrage und einem gleichwertigen JOIN normalerweise keinen Leistungsunterschied, nur bei manchen Existenzprüfungen ist der JOIN im Vorteil. Prüfen Sie bei großen Datenmengen den Ausführungsplan Ihrer Datenbank, statt sich auf Faustregeln zu verlassen.
Kann ich mehrere Spalten aus einem Subselect verwenden?
Ja, wenn das Subselect im FROM steht. Dort liefert es eine abgeleitete Tabelle, deren Spalten Sie über den Alias ansprechen. Hinter einem Vergleichsoperator in WHERE erwartet die Datenbank dagegen in der Regel genau eine Spalte.
Warum liefert meine Abfrage mit NOT IN keine Ergebnisse?
Meist enthält die Unterabfrage mindestens einen NULL-Wert. Dann kann NOT IN für keine Zeile "wahr" ergeben, und das Ergebnis bleibt leer. Verwenden Sie NOT EXISTS oder ergänzen Sie in der Unterabfrage WHERE spalte IS NOT NULL.
Quellen
Empfehlungen