Appearance
Kapitel 10: Unterabfragen (Subqueries)
Unterabfrage (Subquery) ist eine Abfrage, die innerhalb einer anderen Abfrage verwendet wird.
Hauptabfrage (äußere Abfrage)
↓
SELECT ... WHERE feld IN (Unterabfrage)
↑
Innere Abfrage (wird zuerst ausgeführt)10.1 Was ist eine Unterabfrage?
Einfaches Beispiel:
sql
-- Frage: Welche Benutzer haben eine Bestellung getätigt?
-- Ohne Unterabfrage (mit JOIN):
SELECT DISTINCT users.username
FROM users
INNER JOIN orders ON users.user_id = orders.user_id;
-- MIT Unterabfrage:
SELECT username
FROM users
WHERE user_id IN (SELECT DISTINCT user_id FROM orders);Vorteile von Unterabfragen:
- ✅ Logisch getrennt (einfacher zu verstehen)
- ✅ Keine
DISTINCTnötig (bei manchen Fällen) - ✅ Flexibler bei komplexen Bedingungen
10.2 Verwendung in WHERE (Häufigste Verwendung) ⭐⭐
1. Mit IN / NOT IN
sql
-- Benutzer, die EINE Bestellung haben
SELECT username
FROM users
WHERE user_id IN (SELECT user_id FROM orders);
-- Benutzer, die KEINE Bestellung haben
SELECT username
FROM users
WHERE user_id NOT IN (SELECT user_id FROM orders);Beispiel aus der Praxis:
sql
-- Produkte, die bereits bestellt wurden
SELECT produkt_name
FROM products
WHERE produkt_id IN (SELECT produkt_id FROM orders);
-- Produkte, die NIE bestellt wurden (Lagerüberbestand!)
SELECT produkt_name
FROM products
WHERE produkt_id NOT IN (SELECT produkt_id FROM orders);2. Mit Vergleichsoperatoren (=, >, <, etc.)
sql
-- Benutzer mit dem höchsten Umsatz
SELECT username
FROM users
WHERE user_id = (
SELECT user_id
FROM orders
GROUP BY user_id
ORDER BY SUM(preis * menge) DESC
LIMIT 1
);Wichtig: Wenn du = verwendest, muss die Unterabfrage genau einen Wert zurückgeben!
3. Mit EXISTS / NOT EXISTS
EXISTS prüft, ob Datensätze in der Unterabfrage existieren.
sql
-- Benutzer, die mindestens EINE Bestellung haben
SELECT username
FROM users u
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.user_id = u.user_id
);
-- Benutzer, die KEINE Bestellung haben
SELECT username
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.user_id = u.user_id
);Vorteil von EXISTS: Sehr schnell bei großen Datenmengen!
10.3 Verwendung in FROM (Temporäre Tabelle)
Eine Unterabfrage im FROM-Teil wird wie eine temporäre Tabelle behandelt.
sql
-- Durchschnittsalter pro Stadt berechnen, dann Städte über dem Gesamtdurchschnitt finden
SELECT stadt, durchschnittsalter
FROM (
SELECT stadt, AVG(alter) AS durchschnittsalter
FROM users
GROUP BY stadt
) AS stadt_durchschnitte
WHERE durchschnittsalter > (SELECT AVG(alter) FROM users);Erklärung:
- Innere Abfrage: Berechnet Durchschnittsalter pro Stadt → temporäre Tabelle
stadt_durchschnitte - Äußere Abfrage: Filtert die temporäre Tabelle
Wichtig: Eine temporäre Tabelle muss einen Alias haben (AS stadt_durchschnitte)!
10.4 Vergleich: Unterabfragen vs. Tabellenverbindungen
| Unterabfrage (Subquery) | Tabellenverbindung (JOIN) | |
|---|---|---|
| Lesbarkeit | Besser (logisch getrennt) | Schlechter (komplexer) |
| Geschwindigkeit | Langsamer (meistens) | Schneller (meistens) |
| Komplexität | Einfacher bei einfachen Fällen | Besser bei komplexen Abfragen |
| Empfehlung | Für Einsteiger | Für Fortgeschrittene |
Beispiel: Gleichwertige Abfragen
Mit Unterabfrage:
sql
SELECT username
FROM users
WHERE user_id IN (SELECT user_id FROM orders);Mit JOIN:
sql
SELECT DISTINCT users.username
FROM users
INNER JOIN orders ON users.user_id = orders.user_id;Beide geben das gleiche Ergebnis!
10.5 Praxisbeispiel: Komplexe Bedingungsabfragen
Szenario: Eine Online-Shop-Datenbank analysieren.
Schritt 1: Testdaten vorbereiten
sql
USE shop_db;
-- Unterabfrage: Produkte finden, die teurer als der Durchschnitt sind
SELECT produkt_name, preis
FROM products
WHERE preis > (SELECT AVG(preis) FROM products)
ORDER BY preis DESC;Schritt 2: Benutzer mit überdurchschnittlichem Umsatz
sql
SELECT
u.username,
SUM(o.menge * o.preis) AS umsatz
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id
GROUP BY u.user_id, u.username
HAVING umsatz > (
SELECT AVG(gesamt_umsatz)
FROM (
SELECT SUM(menge * preis) AS gesamt_umsatz
FROM orders
GROUP BY user_id
) AS umsatz_pro_user
);Häufige Fehler für Einsteiger
Fehler 1: Unterabfrage gibt mehrere Werte zurück (bei =)
sql
-- ❌ Falsch: Unterabfrage gibt mehrere user_id zurück
SELECT username
FROM users
WHERE user_id = (SELECT user_id FROM orders); -- FEHLER!
-- ✅ Richtig: IN verwenden
SELECT username
FROM users
WHERE user_id IN (SELECT user_id FROM orders);Fehler 2: Temporäre Tabelle ohne Alias
sql
-- ❌ Falsch: Kein Alias
SELECT *
FROM (SELECT stadt, COUNT(*) FROM users GROUP BY stadt);
-- ✅ Richtig: Mit Alias
SELECT *
FROM (SELECT stadt, COUNT(*) AS anzahl FROM users GROUP BY stadt) AS temp;Fehler 3: Unterabfrage in SELECT (Performance-Problem)
sql
-- ❌ Langsam (Unterabfrage wird für jede Zeile ausgeführt!)
SELECT
username,
(SELECT COUNT(*) FROM orders WHERE user_id = users.user_id) AS anzahl_bestellungen
FROM users;
-- ✅ Schneller (mit JOIN)
SELECT
u.username,
COUNT(o.bestell_id) AS anzahl_bestellungen
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
GROUP BY u.user_id, u.username;Zusammenfassung
In diesem Kapitel hast du gelernt:
- ✅ Was Unterabfragen (Subqueries) sind
- ✅ Unterabfragen in
WHEREzu verwenden (IN,NOT IN,EXISTS) - ✅ Unterabfragen in
FROMzu verwenden (temporäre Tabellen) - ✅ Den Unterschied zwischen Unterabfragen und
JOINs - ✅ Wann Unterabfragen sinnvoll sind (und wann nicht)
Übungen
- Theorie: Was ist der Unterschied zwischen
INundEXISTS? - Praxis: Finde alle Produkte, die nicht bestellt wurden (
NOT IN) - Praxis: Finde die Benutzer mit dem höchsten Umsatz (Unterabfrage mit
LIMIT 1) - Praxis: Erstelle eine temporäre Tabelle, die das Durchschnittsalter pro Stadt berechnet
Nächstes Kapitel
Im nächsten Kapitel lernen wir Transaktionen und Indizes - wichtig für Datensicherheit und Performance!
