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 ist nicht in jedem System verfügbar (z. B. SQLite). Oft kann man ihn durch LEFT JOIN „umdrehen“.

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';

RIGHT JOIN ist in manchen Systemen nicht verfügbar (z. B. SQLite). Sie können ihn meist ersetzen, indem Sie die Tabellen tauschen und LEFT JOIN verwenden.

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

Quiz: JOINs (INNER, LEFT, RIGHT)

1. (LZ2) Was liefert ein INNER JOIN?

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

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

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

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.