Unterabfragen
Kurz gesagt: Eine Unterabfrage ist ein SELECT in einer anderen Abfrage, das einen Wert oder eine Liste für WHERE liefert und mit IN, EXISTS oder NOT EXISTS benutzt wird.
Problem
Über dem Durchschnitt
Die Shopleitung will die Produkte sehen, die mehr kosten als der Durchschnittspreis im Shop. Du kennst schon zwei Abfragen: Die eine findet den Durchschnittspreis, die andere sucht Produkte über einer bestimmten Zahl.
Du könntest die erste ausführen, die Zahl kopieren und die zweite ausführen. Aber morgen sind die Preise andere, und die kopierte Zahl ist veraltet.
Die Idee
Unterabfrage
Zuerst der Durchschnittspreis:
Jetzt dieselbe Abfrage, in Klammern, anstelle der Zahl:
Eine Abfrage in Klammern innerhalb einer anderen Abfrage heißt Unterabfrage (Subquery). Die Datenbank führt sie zuerst aus, bekommt 7,036 und prüft dann jedes Produkt mit price > 7.036. Es kommen acht Produkte heraus.
Diese Unterabfrage liefert eine Zahl, deshalb steht sie dort, wo eine Zahl stehen würde.
Genauer, für Neugierige
Wenn eine Unterabfrage in einem Vergleich mehrere Zeilen liefert, nimmt SQLite stillschweigend die erste. Kategorie 8 („Küche“) hat drei Produkte, aber die Abfrage vergleicht nur mit einem davon:
Welche Zeile „die erste“ ist, wird nicht versprochen. PostgreSQL und SQL Server geben in diesem Fall einen Fehler aus: Die Unterabfrage habe mehr als eine Zeile geliefert. Wenn eine Unterabfrage mehrere Zeilen liefern kann, nimm IN.
Probier es aus
Teurer als der Kaschkawal
Zeige Name und Preis der Produkte, die mehr kosten als der Kaschkawal. Tippe seinen Preis nicht von Hand ein: Hol ihn mit einer Unterabfrage.
Lösung
SELECT name, price FROM products
WHERE price > (SELECT price FROM products WHERE name = 'Kaschkawal');WHERE price > (SELECT price FROM products WHERE name = 'Kaschkawal'). Der Käse kostet 9,50 €, und es gibt 6 teurere Produkte, vom Schneidebrett (11,40 €) bis zum Messerset (39 €).
Die Idee
IN mit einer Liste aus einer Abfrage
IN mit einer von Hand getippten Liste kennst du schon: city IN ('Varna', 'Burgas'). Die Liste kann auch aus einer Unterabfrage kommen.
Welche Kunden haben im Oktober 2026 bestellt? Ihre Nummern stehen in orders, ihre Namen in customers:
Die Unterabfrage liefert die Nummern der Kunden mit Bestellungen ab dem 1. Oktober. Die äußere Abfrage zeigt die Kunden, deren id in dieser Liste steht.
Unterabfragen lassen sich ineinander verschachteln. Jeder Schritt ist eine eigene Abfrage, die du für sich ausführen und prüfen kannst.
Probier es aus
Wer Kaffee kauft
Der Kaffeelieferant bringt eine neue Sorte heraus und will die Leute anschreiben, die schon „Kaffeebohnen“ gekauft haben. Zeige ihre Namen. Das Feld beginnt mit dem ersten Schritt: der Produktnummer.
Lösung
SELECT name FROM customers
WHERE id IN (
SELECT customer_id FROM orders
WHERE id IN (
SELECT order_id FROM order_items
WHERE product_id = (SELECT id FROM products WHERE name = 'Kaffeebohnen')
)
);Drei Schritte von innen nach außen: die Produktnummer (17), die Bestellungen mit einer Zeile dafür (order_items) und die Kunden dieser Bestellungen (orders). Es kommen drei Personen heraus: Maria, Viktoria und Simona. Ein JOIN funktioniert auch: Verglichen wird das Ergebnis, nicht die Methode.
Die Idee
EXISTS
Manchmal ist dir egal, was die Unterabfrage liefert, sondern nur, ob sie überhaupt etwas liefert. Welche Kunden haben noch keine Bestellung?
EXISTS (…) ist wahr, wenn die Unterabfrage mindestens eine Zeile liefert. NOT EXISTS ist das Gegenteil. Was in der Unterabfrage nach SELECT steht, ist egal, deshalb steht dort SELECT 1.
Hier schaut die Unterabfrage auf die äußere Zeile: o.customer_id = c.id, wobei c der Kunde ist, den die äußere Abfrage gerade prüft. Das ist eine neue Idee, und die nächste Lektion sieht sie sich genau an.
Sieh es dir an
NOT IN und ein einziges NULL
Die Shopleitung ordnet das Menü der Website und fragt, welche Kategorien gar keine Produkte haben. Sie erwartet „Lebensmittel“ und „Haushalt“: Dort liegen nur Unterkategorien. Hier der erste Versuch:
Das ist die Falle aus der NULL-Lektion, aber jetzt kommt NULL aus der Unterabfrage und ist in der Abfrage nicht zu sehen. NOT EXISTS vergleicht keine Werte: Es fragt, ob es so ein Produkt gibt, und NULL stört dabei nicht:
Wenn die Unterabfrage über eine Spalte läuft, die NULL sein kann, nimm NOT EXISTS oder ergänze die Unterabfrage um WHERE category_id IS NOT NULL.
Übung
Zwei Aufgaben
1. Das Qualitätsteam will die Kunden anrufen, die mindestens eine stornierte Bestellung haben.
Zeige die Namen der Kunden mit mindestens einer stornierten Bestellung.
Lösung
SELECT name FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.id AND o.status = 'cancelled'
);WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.status = 'cancelled'). Sieben Kunden. Es geht auch mit id IN (SELECT customer_id FROM orders WHERE status = 'cancelled').
2. Das Marketing bereitet eine Kampagne „Schreib eine Bewertung“ für die Produkte vor, die noch niemand bewertet hat.
Zeige die Namen der Produkte ohne eine einzige Bewertung (die Tabelle reviews).
Lösung
SELECT name FROM products p
WHERE NOT EXISTS (SELECT 1 FROM reviews r WHERE r.product_id = p.id);WHERE NOT EXISTS (SELECT 1 FROM reviews r WHERE r.product_id = p.id). 12 Produkte, darunter die Geschenkkarte.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: IN, BETWEEN und LIKE · NULL: kein Wert · Aggregatfunktionen: COUNT, SUM, AVG · JOIN: zwei Tabellen in einer Abfrage
- Geht weiter in: Korrelierte Unterabfragen · Eine Auswertung in Schritten: WITH · Bäume und Folgen: WITH RECURSIVE · Fensterfunktionen
- Aufgaben zum Thema: Unterabfragen und Fensterfunktionen