SQL-JOINs verständlich – INNER JOIN vs. LEFT JOIN
Zwei Tabellen zusammenführen ist Alltag – und die häufigste Fehlerquelle ist der falsche JOIN-Typ. Ich erkläre dir INNER JOIN und LEFT JOIN an einem konkreten Beispiel und warum ein LEFT JOIN dir Zeilen „verstecken" kann, wenn du im WHERE nicht aufpasst.
Sobald Daten auf mehrere Tabellen verteilt sind, brauchst du JOINs, um sie in einer Abfrage zusammenzuführen. Nehmen wir zwei Tabellen: kunden und bestellungen (jede Bestellung verweist über kunde_id auf einen Kunden).
INNER JOIN – nur Treffer auf beiden Seiten
SELECT k.name, b.betrag
FROM kunden k
INNER JOIN bestellungen b ON b.kunde_id = k.id;Ein INNER JOIN liefert nur Zeilen, für die es auf beiden Seiten einen passenden Eintrag gibt. Ein Kunde ohne Bestellung taucht hier nicht auf. Das ist genau richtig, wenn du z. B. „alle bezahlten Bestellungen mit Kundennamen" willst.
LEFT JOIN – alle Kunden, auch ohne Bestellung
SELECT k.name, b.betrag
FROM kunden k
LEFT JOIN bestellungen b ON b.kunde_id = k.id;Ein LEFT JOIN behält jede Zeile der linken Tabelle (kunden) – auch die ohne passende Bestellung. Für diese Kunden ist b.betrag dann schlicht NULL. Perfekt für Fragen wie „welche Kunden haben noch nie bestellt?":
SELECT k.name
FROM kunden k
LEFT JOIN bestellungen b ON b.kunde_id = k.id
WHERE b.id IS NULL; -- kein Treffer rechts → Kunde ohne BestellungDie Falle: Bedingung im WHERE statt im ON
Ein Klassiker, der schon vielen die Auswertung verhagelt hat. Du willst einen LEFT JOIN, filterst aber die rechte Tabelle im WHERE:
-- ⚠️ verhält sich wie ein INNER JOIN!
SELECT k.name, b.betrag
FROM kunden k
LEFT JOIN bestellungen b ON b.kunde_id = k.id
WHERE b.betrag > 100;Sobald b.betrag NULL ist (Kunde ohne Bestellung), ist NULL > 100 nicht wahr – die Zeile fliegt raus. Der LEFT JOIN ist damit wirkungslos. Willst du die Bedingung nur auf die Verknüpfung anwenden, gehört sie ins ON:
SELECT k.name, b.betrag
FROM kunden k
LEFT JOIN bestellungen b
ON b.kunde_id = k.id AND b.betrag > 100; -- Kunden bleiben erhaltenMerksatz
- INNER JOIN: Schnittmenge – nur was zusammenpasst.
- LEFT JOIN: linke Tabelle komplett, rechte wo vorhanden, sonst
NULL. - Filter auf die optionale (rechte) Seite gehört ins
ON, nicht insWHERE.
Das gilt in SQLite, PostgreSQL, MySQL und SQL Server gleich. Wenn du ein Projekt mit ordentlich modellierter Datenbank brauchst, baue ich dir Struktur und Abfragen dahinter – meld dich über bymw.de.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
Wie viel mehr als gestern? Differenz zur Vorzeile mit LAG() in SQL
„Wie viele Besucher heute im Vergleich zu gestern?" – der Reflex ist ein Self-Join auf die Vorzeile. Mit der Fensterfunktion LAG() bekommst du den Wert der vorherigen Zeile direkt daneben, ohne die Tabelle ein zweites Mal anzufassen.
WITH RECURSIVE – Kategoriebäume und Kommentar-Threads in einer einzigen Abfrage
Kategorien mit Unterkategorien, Kommentare mit Antworten, Ordner in Ordnern: Die meisten holen sich so etwas mit einer Schleife und einer Abfrage pro Ebene. Eine rekursive CTE holt den ganzen Baum in einem Rutsch – inklusive Tiefe und Pfad.
UNION vs. UNION ALL – zwei Abfragen zusammenlegen, ohne Zeilen zu verlieren
UNION entfernt Duplikate. Das klingt hilfreich, kostet aber eine komplette Sortierung – und wirft dir stillschweigend echte Zeilen weg, die zufällig identisch aussehen. UNION ALL ist fast immer die richtige Wahl.