SemaTrain Logo Ein Fachportal von SemaTrain

JOINs: INNER, LEFT, RIGHT

Jetzt wird’s „relational“: Sie verknüpfen Tabellen sauber mit JOINs. Ziel: Termine + Kurse + Trainer in einer Abfrage – verständlich, korrekt und reproduzierbar.

Hinweis: RIGHT JOIN wird von aktuellen SQLite-Versionen unterstützt (seit 3.39.0). Für Portabilität oder ältere SQLite-Versionen lässt sich dieselbe Logik häufig als LEFT JOIN mit vertauschter Tabellenreihenfolge formulieren.

Lernziel

Nach dieser Lektion können Sie

SQL Grundlagen Schulung – Kursbezug

Dieses Kapitel ist Teil des Lernpfads zur SQL Grundlagen Schulung. Termine & Buchung laufen über SemaTrain.de.

Wir arbeiten mit dem Schema: kurse, trainer, termine.

Worum geht’s?

Lehr-/Lernziele

Denkmodell (Venn-Logik in Textform)

INNER JOIN: A ∩ B (nur passende Paare) LEFT JOIN: A ⟕ B (alles aus A, B wenn passend, sonst NULL) RIGHT JOIN: A ⟖ B (alles aus B, A wenn passend, sonst NULL)

Basis: INNER JOIN (Termine + Kursname)

Wir holen zu jedem Termin den Kurstitel – das ist der klassische FK→PK-Join.

INNER JOIN: termine ↔ kurse (SQL)
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  k.kurs_name
FROM termine AS t
INNER JOIN kurse AS k
  ON t.kurs_id = k.kurs_id
ORDER BY t.start_datum ASC;
Warum Aliases?
  • Spalten sind eindeutiger (t.ort statt nur ort).
  • Abfragen bleiben lesbar, besonders mit vielen Tabellen.
  • Sie vermeiden „ambiguous column name“-Fehler.

JOIN über 3 Tabellen (Termine + Kurse + Trainer)

Wichtig: INNER JOIN auf trainer lässt Termine ohne Trainer verschwinden. Wenn Trainer optional ist (wie in eurem Lernpfad), braucht ihr meist LEFT JOIN.

3 Tabellen: INNER vs LEFT (Trainer optional) (SQL)
-- Variante A: Trainer ist Pflicht (Termine ohne Trainer fallen raus)
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  k.kurs_name,
  tr.name AS trainer
FROM termine AS t
INNER JOIN kurse   AS k  ON t.kurs_id    = k.kurs_id
INNER JOIN trainer AS tr ON t.trainer_id = tr.trainer_id
ORDER BY t.start_datum ASC;

-- Variante B (praxisnäher): Trainer optional → LEFT JOIN
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  k.kurs_name,
  tr.name AS trainer
FROM termine AS t
INNER JOIN kurse   AS k  ON t.kurs_id    = k.kurs_id
LEFT JOIN trainer  AS tr ON t.trainer_id = tr.trainer_id
ORDER BY t.start_datum ASC;

LEFT JOIN: wenn rechts optional ist

Typischer Fall: Es gibt einen Termin, aber noch keinen Trainer. Mit LEFT JOIN behalten Sie die linke Seite komplett.

LEFT JOIN: termine ↔ trainer (optional) (SQL)
-- Alle Termine, Trainer-Name wenn vorhanden (sonst NULL)
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  tr.name AS trainer
FROM termine AS t
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id
ORDER BY t.start_datum ASC;
Häufige Falle: WHERE macht LEFT JOIN kaputt

Wenn Sie Bedingungen auf der rechten Tabelle in WHERE setzen, werden NULL-Zeilen rausgefiltert – das wirkt wie ein INNER JOIN.

LEFT JOIN + Filter: WHERE vs ON (SQL)
-- FALLE: wird effektiv zu INNER JOIN, weil tr.name NULL rausfliegt
SELECT t.termin_id, tr.name
FROM termine AS t
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id
WHERE tr.name = 'Mathias Ellmann';

-- BESSER: Filter in die JOIN-Bedingung verschieben (NULLs bleiben erhalten)
SELECT t.termin_id, tr.name
FROM termine AS t
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id
 AND tr.name = 'Mathias Ellmann';

Aktuelle SQLite-Versionen unterstützen RIGHT JOIN seit 3.39.0. Für Portabilität oder ältere SQLite-Versionen können Sie dieselbe Logik meist als LEFT JOIN mit vertauschter Tabellenreihenfolge formulieren.

RIGHT JOIN vs. LEFT JOIN (Ersatz) (SQL)
-- RIGHT JOIN (falls unterstützt): alle Kurse, auch ohne Termin
SELECT
  k.kurs_id,
  k.kurs_name,
  t.termin_id,
  t.start_datum
FROM termine AS t
RIGHT JOIN kurse AS k
  ON t.kurs_id = k.kurs_id;

-- Ersatz (funktional ähnlich): Tabellen tauschen + LEFT JOIN
SELECT
  k.kurs_id,
  k.kurs_name,
  t.termin_id,
  t.start_datum
FROM kurse AS k
LEFT JOIN termine AS t
  ON t.kurs_id = k.kurs_id;

Typische Join-Stolperfallen

Praxisaufgabe

  1. Geben Sie alle Termine inkl. Kursname aus (INNER JOIN).
  2. Erweitern Sie um Trainername (INNER JOIN auf trainer).
  3. Ändern Sie die Abfrage so, dass Termine ohne Trainer trotzdem erscheinen (LEFT JOIN).
  4. Sortieren Sie nach Startdatum (ASC) und Ort (ASC).
Lösungsvorschlag anzeigen
Lösung: INNER + LEFT JOIN, sortiert (SQL)
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  k.kurs_name,
  tr.name AS trainer
FROM termine AS t
INNER JOIN kurse AS k
  ON t.kurs_id = k.kurs_id
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id
ORDER BY t.start_datum ASC, t.ort ASC;

Kurz-Takeaways

Primärreferenzen

Geprüfte SQL-Referenzen

Die fachlichen Aussagen dieser Lektion werden gegen Normkontext und zuständige Primärdokumentationen der jeweiligen Datenbanksysteme geprüft. Erklärungen, Beispiele, Übungen, Quizfragen und Lösungen sind eigenständige SemaTrain-Inhalte.

Lektionsprofil: JOINs und relationale Verknüpfungen

Kursbasis: Gemeinsamer SQL-Kern mit explizit gekennzeichneten Varianten für SQLite 3.53.4, PostgreSQL 18 und MySQL 8.4 LTS.

Quellenprüfung: 2026-09-18

Einordnung: JOIN-Semantik wird systemübergreifend erklärt. SQLite unterstützt RIGHT und FULL OUTER JOIN seit Version 3.39.0.

ISO/IEC 9075-1:2023 Information technology — Database languages SQL — Part 1: Framework

Quellentyp: Norm-/Standardreferenz

Projekt / Referenzstelle: ISO/IEC 9075

Ersteller / Maintainer: ISO/IEC JTC 1/SC 32

Versionsbezug: ISO/IEC 9075-1:2023

Aussagebereich: SQL-Framework, Begriffe und Standardkontext.

Offizielle Primärreferenz

SQLite SELECT and JOIN syntax

Quellentyp: Projekt-Primärdokumentation

Projekt / Referenzstelle: SQLite

Ersteller / Maintainer: SQLite Developers

Versionsbezug: SQLite 3.53.4

Aussagebereich: JOIN-Grammatik und aktuelle JOIN-Unterstützung.

Offizielle Primärreferenz

SQLite Release 3.39.0

Quellentyp: Projekt-Release-Dokumentation

Projekt / Referenzstelle: SQLite

Ersteller / Maintainer: SQLite Developers

Versionsbezug: SQLite 3.39.0

Aussagebereich: Einführung von RIGHT und FULL OUTER JOIN.

Offizielle Primärreferenz

PostgreSQL 18 Table Expressions

Quellentyp: Projekt-Primärdokumentation

Projekt / Referenzstelle: PostgreSQL

Ersteller / Maintainer: PostgreSQL Global Development Group

Versionsbezug: PostgreSQL 18

Aussagebereich: INNER, LEFT, RIGHT und FULL OUTER JOIN sowie ON/USING.

Offizielle Primärreferenz

MySQL 8.4 JOIN Clause

Quellentyp: Hersteller-Primärdokumentation

Projekt / Referenzstelle: MySQL

Ersteller / Maintainer: Oracle Corporation

Versionsbezug: MySQL 8.4 LTS

Aussagebereich: INNER, LEFT und RIGHT JOIN, ON, USING und Portabilitätshinweise.

Offizielle Primärreferenz

SQL Quellen und Methodik

Quiz: JOINs (INNER, LEFT, RIGHT)

1. (LZ2) Was liefert ein INNER JOIN?

Einfachauswahl – genau eine Antwort ist richtig.

2. (LZ2) Was ist das Kennzeichen eines LEFT JOIN?

Einfachauswahl – genau eine Antwort ist richtig.

3. (LZ3) Warum ist WHERE tr.name = ... nach einem LEFT JOIN oft problematisch?

Einfachauswahl – genau eine Antwort ist richtig.

4. (LZ1) Welche Join-Bedingung ist im Schulungsschema korrekt, um Termine mit Kursen zu verbinden?

Einfachauswahl – genau eine Antwort ist richtig.

Praxisaufgabe

Mini-Projekt: Termin-Report „Kurse + Trainer“ (JOINs in Stufen)

Sie bauen eine Reporting-Abfrage, die man später als View speichern könnte: Termine mit Kursname und (optionalem) Trainer. Fokus: saubere Join-Bedingungen, passende Join-Art und typische Fallen vermeiden.

Beitrag zu den Lehr-/Lernzielen: LZ1 (Join-Bedingung), LZ2 (Join-Art), LZ3 (Filterfalle), LZ4 (Lesbarkeit/Spaltenwahl).

Stufe A – Basis-Report (Termine + Kursname)

Stufe B – Trainer dazu (INNER vs. LEFT)

Stufe C – Die Standardfalle (LEFT JOIN + WHERE)

Stufe D – Report-Qualität

Bonus – View-ready (nur als Vorschau)

Musterlösung anzeigen
Projektlösung: Termin-Report (JOINs in Stufen) (SQL)
-- ==========================================================
-- Stufe A – Basis: Termine + Kursname (INNER JOIN)
-- ==========================================================
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  k.kurs_name
FROM termine AS t
INNER JOIN kurse AS k
  ON t.kurs_id = k.kurs_id
ORDER BY t.start_datum ASC, t.ort ASC;

-- ==========================================================
-- Stufe B1 – 3 Tabellen mit INNER JOIN (Trainer ist Pflicht)
-- -> Termine ohne Trainer fallen raus
-- ==========================================================
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  k.kurs_name,
  tr.name AS trainer
FROM termine AS t
INNER JOIN kurse   AS k  ON t.kurs_id    = k.kurs_id
INNER JOIN trainer AS tr ON t.trainer_id = tr.trainer_id
ORDER BY t.start_datum ASC, t.ort ASC;

-- ==========================================================
-- Stufe B2 – 3 Tabellen mit LEFT JOIN (Trainer optional)
-- -> Termine bleiben erhalten, Trainer ggf. NULL
-- ==========================================================
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  k.kurs_name,
  tr.name AS trainer
FROM termine AS t
INNER JOIN kurse AS k
  ON t.kurs_id = k.kurs_id
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id
ORDER BY t.start_datum ASC, t.ort ASC;

-- ==========================================================
-- Stufe C – Standardfalle: LEFT JOIN + WHERE auf rechter Tabelle
-- ==========================================================

-- FALLE (macht LEFT JOIN faktisch zu INNER JOIN, weil NULL rausfliegt):
SELECT
  t.termin_id,
  t.start_datum,
  k.kurs_name,
  tr.name AS trainer
FROM termine AS t
INNER JOIN kurse AS k
  ON t.kurs_id = k.kurs_id
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id
WHERE tr.name = 'Mathias Ellmann'
ORDER BY t.start_datum ASC;

-- BESSER (Filter in ON, NULL-Zeilen bleiben erhalten):
SELECT
  t.termin_id,
  t.start_datum,
  k.kurs_name,
  tr.name AS trainer
FROM termine AS t
INNER JOIN kurse AS k
  ON t.kurs_id = k.kurs_id
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id
 AND tr.name = 'Mathias Ellmann'
ORDER BY t.start_datum ASC;

-- ==========================================================
-- Stufe D – Report-Qualität: Ausgabe „schön“ machen
-- ==========================================================
SELECT
  t.termin_id,
  t.start_datum,
  t.ort,
  t.format,
  k.kurs_name,
  COALESCE(tr.name, '(noch offen)') AS trainer
FROM termine AS t
INNER JOIN kurse AS k
  ON t.kurs_id = k.kurs_id
LEFT JOIN trainer AS tr
  ON t.trainer_id = tr.trainer_id
ORDER BY t.start_datum ASC, t.ort ASC;

-- ==========================================================
-- Bonus – View-ready (Kapitel 06): als View speichern (optional)
-- ==========================================================
-- CREATE VIEW v_termin_report AS
--   <Finale Query aus Stufe D (meist ohne ORDER BY)> ;

Ausblick: Im nächsten Kapitel (Aggregation) zählen wir z. B. Termine pro Kurs oder Orte mit den meisten Terminen – und sehen, warum 1:n-JOINs „Duplikate“ erzeugen können.