Zum Inhalt springen

7. Joins

Zu Zen-Modus wechseln

In relationalen Datenbanken sind Daten typischerweise über mehrere logisch zusammengehörige Tabellen verteilt. Um eine brauchbare Ergebnismenge zu erzeugen, müssen diese Datenquellen innerhalb einer Abfrage kombiniert werden. Primär- und Fremdschlüssel spielen hier eine entscheidende Rolle, da sie die Verbindung zwischen Datensätzen über mehrere Tabellen hinweg vereinfachen. Das Verknüpfen verschiedener Tabellen ist ein grundlegender Aspekt des relationalen Datenmodells.

Die SELECT-Anweisung kann auf verschiedene Weise erweitert werden, um den gleichzeitigen Zugriff auf mehrere Tabellen zu ermöglichen. Diese Abfragen können zwei oder sogar mehr Tabellen auf einmal ansprechen.

Die Datenbestände einzelner Tabellen stellen Mengen von Datensätzen (Tupeln) dar. Mithilfe von SQL-Anweisungen lassen sich diese Mengen auf unterschiedliche Weise kombinieren.

Werden zwei oder mehr vereinigungsverträgliche (union-compatible) Mengen zusammengeführt, enthält der resultierende Datenbestand alle Datensätze aus allen beteiligten Tabellen. Es ist auch möglich, Schnittmengen zu bilden, um nur jene Datensätze zu ermitteln, die in allen beteiligten Tabellen vorhanden sind. Die folgenden drei Mengenoperationen werden beim Verknüpfen von Tabellen am häufigsten verwendet:

  • Vereinigung (UNION): Fügt zwei oder mehr Tabellen zu einem einzigen Datenbestand zusammen. Duplikate werden standardmäßig entfernt, können aber mit UNION ALL beibehalten werden. Die Vereinigung entspricht dem “Vereinigungs”-Operator in der relationalen Algebra.
  • Schnittmenge (INTERSECT): Wählt nur jene Datensätze aus zwei oder mehr Mengen aus, die in allen Mengen identisch sind. Die Schnittmenge entspricht dem “Durchschnitts”-Operator in der relationalen Algebra.
  • Differenz (EXCEPT / MINUS): Ermittelt alle Datensätze, die in einer Menge enthalten sind, aber nicht in der anderen.

Das Verknüpfen mehrerer Tabellen wird als Join bezeichnet. Joins werden verwendet, um Datensätze aus zwei oder mehr Tabellen zu einer einzigen Ergebnismenge zu kombinieren.

Für die weiteren Join-Beispiele verwenden wir die beiden Tabellen students und grades. Beachten Sie, dass der Schüler “Charlie” noch keine Noten hat und dass es einen Noteneintrag (ID 5) mit einer student_id gibt, die in unserer Schülertabelle nicht existiert - um besser zu zeigen, wie Joins mit fehlenden Übereinstimmungen umgehen.

student_idfirst_namelast_name
1AliceAdelson
2BobBurger
3CharlieCheck
grade_idstudent_idsubjectscore
11SEW1
21INSY2
32MEDT3
42Math1
599English2

Hinweis: Die student_id 99 in der Tabelle grades existiert nicht in der Tabelle students, was wichtig wird, wenn wir betrachten, wie verschiedene Join-Typen mit nicht übereinstimmenden Datensätzen umgehen. Ja, das ist absichtlich so, um das Verhalten von Joins bei nicht übereinstimmenden Datensätzen zu veranschaulichen. Dadurch können wir sehen, wie verschiedene Join-Typen (Inner Join, Left Join, Right Join, Full Outer Join) Fälle behandeln, in denen es Datensätze in einer Tabelle gibt, die keine passende Entsprechung in der anderen Tabelle haben. Es hilft, das Konzept der Joins deutlicher zu demonstrieren, indem die Auswirkungen nicht übereinstimmender Datensätze auf die resultierende Datenmenge gezeigt werden. Normalerweise würde man in einer gut gepflegten Datenbank erwarten, dass Fremdschlüssel-Constraints solche Diskrepanzen verhindern, aber zu Lehrzwecken erlaubt uns dieser Aufbau, das Verhalten von Joins in verschiedenen Szenarien zu erkunden.

Wenn Sie dies in Ihrer Umgebung testen möchten, verwenden Sie den folgenden Code:

CREATE TABLE students (
student_id INT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50)
);
CREATE TABLE grades (
grade_id INT PRIMARY KEY,
student_id INT,
subject VARCHAR(10),
score INT
);
INSERT INTO students VALUES
(1, 'Alice', 'Adelson'),
(2, 'Bob', 'Burger'),
(3, 'Charlie', 'Check');
INSERT INTO grades VALUES
(1, 1, 'SEW', 1),
(2, 1, 'INSY', 2),
(3, 2, 'MEDT', 3),
(4, 2, 'Math', 1),
(5, 99, 'English', 2);

Der Inner Join ist der häufigste Join-Typ. Er gibt nur die Datensätze zurück, die in beiden Tabellen übereinstimmende Werte haben. Wenn eine Zeile in der ersten Tabelle keine entsprechende Übereinstimmung in der zweiten Tabelle hat (oder umgekehrt), erscheint diese Zeile nicht in der Ergebnismenge.

In unserem Beispiel verknüpfen wir die Tabelle students mit der Tabelle grades über die student_id.

SELECT s.first_name, s.last_name, g.subject, g.score
FROM students s
INNER JOIN grades g ON s.student_id = g.student_id;
first_namelast_namesubjectscore
AliceAdelsonSEW1
AliceAdelsonINSY2
BobBurgerMEDT3
BobBurgerMath1
  • Charlie Check fehlt im Ergebnis, weil er keine Einträge in der Tabelle grades hat.
  • Note ID 5 (English) fehlt, weil die student_id 99 nicht in der Tabelle students existiert.
  • Der INNER JOIN stellt sicher, dass das Ergebnis nur “vollständige” Datenpaare enthält, bei denen ein Schüler eindeutig einer Note zugeordnet ist.

Der Left Join gibt alle Datensätze aus der linken Tabelle (students) und die passenden Datensätze aus der rechten Tabelle (grades) zurück.

Wenn es für einen Datensatz aus der linken Tabelle keine Übereinstimmung gibt, enthält das Ergebnis diesen Datensatz dennoch, allerdings mit NULL-Werten für jede Spalte der rechten Tabelle. Das ist besonders nützlich, wenn Sie Entitäten identifizieren möchten, die keine zugehörigen Daten in einer verknüpften Tabelle haben (z. B. Schüler, die noch keine Noten erhalten haben).

SELECT s.first_name, s.last_name, g.subject, g.score
FROM students s
LEFT JOIN grades g ON s.student_id = g.student_id;
first_namelast_namesubjectscore
AliceAdelsonSEW1
AliceAdelsonINSY2
BobBurgerMEDT3
BobBurgerMath1
CharlieCheckNULLNULL
  • Charlie Check erscheint nun in der Liste. Da er keine Einträge in der Tabelle grades hat, werden die Spalten subject und score mit NULL gefüllt.
  • Note ID 5 (English) fehlt weiterhin. Das liegt daran, dass ein Left Join die linke Tabelle (students) priorisiert und es keinen Schüler mit der ID 99 gibt, der diese Note ins Ergebnis ziehen würde.
  • Dieser Join-Typ ist wesentlich für Berichte, bei denen Sie eine vollständige Liste der Schüler benötigen, unabhängig davon, ob sie ihre Aufgaben erledigt haben.

Der Right Join ist das genaue Spiegelbild des Left Join. Er gibt alle Datensätze aus der rechten Tabelle (grades) und die passenden Datensätze aus der linken Tabelle (students) zurück.

Wenn ein Datensatz in der rechten Tabelle keinen passenden Partner in der linken Tabelle hat, werden die Schülerspalten mit NULL gefüllt. Dies wird oft verwendet, um “verwaiste” Einträge zu finden - Daten, die in einer Untertabelle existieren, aber nicht mehr mit einem Hauptdatensatz verknüpft sind.

SELECT s.first_name, s.last_name, g.subject, g.score
FROM students s
RIGHT JOIN grades g ON s.student_id = g.student_id;
first_namelast_namesubjectscore
AliceAdelsonSEW1
AliceAdelsonINSY2
BobBurgerMEDT3
BobBurgerMath1
NULLNULLEnglish2

  • Die “verwaiste” Note: Note ID 5 (English) erscheint nun im Ergebnis, obwohl die student_id 99 in unserer Tabelle students nicht existiert. Die Namensspalten sind einfach NULL.
  • Charlie Check ist weg: Da Charlie keinen Eintrag in der Tabelle grades hat, wird er ausgeschlossen, weil der Right Join die rechte Seite priorisiert.
  • Praktischer Einsatz: In vielen realen Szenarien ziehen Entwickler es vor, LEFT JOIN zu verwenden und einfach die Tabellenreihenfolge in der Abfrage zu vertauschen, damit die Logik von links nach rechts leichter lesbar bleibt.

Der Full Outer Join kombiniert die Ergebnisse von LEFT JOIN und RIGHT JOIN. Er gibt alle Datensätze zurück, wenn es eine Übereinstimmung in der linken Tabelle (students) oder der rechten Tabelle (grades) gibt.

Wenn es keine Übereinstimmung gibt, enthält die fehlende Seite NULL-Werte. Dieser Join bietet eine vollständige Sicht auf beide Tabellen und zeigt Ihnen übereinstimmende Daten, Schüler ohne Noten und Noten ohne Schüler in einer einzigen Ergebnismenge.

SELECT s.first_name, s.last_name, g.subject, g.score
FROM students s
FULL OUTER JOIN grades g ON s.student_id = g.student_id;
first_namelast_namesubjectscore
AliceAdelsonSEW1
AliceAdelsonINSY2
BobBurgerMEDT3
BobBurgerMath1
CharlieCheckNULLNULL
NULLNULLEnglish2
  • Der “Alles-rein”-Ansatz: Dies ist der einzige Join, der sowohl Charlie Check (den Schüler ohne Noten) als auch die English-Note (die Note ohne Schüler) enthält.
  • Verwendung in Datenbanken: Obwohl sehr mächtig, beachten Sie, dass manche Datenbanksysteme (wie MySQL/MariaDB) FULL OUTER JOIN nicht direkt unterstützen. In diesen Fällen müssen Sie ihn durch eine UNION aus einem LEFT JOIN und einem RIGHT JOIN nachbilden.
  • Datenprüfung (Auditing): Dieser Join eignet sich hervorragend zur Datenbereinigung oder -prüfung, da er fehlende Beziehungen auf beiden Seiten Ihres Datenbankschemas sofort sichtbar macht.

Der Cross Join ist der grundlegendste und weitreichendste Join-Typ. Er erzeugt ein kartesisches Produkt der beiden Tabellen. Das bedeutet, jede einzelne Zeile der ersten Tabelle wird mit jeder einzelnen Zeile der zweiten Tabelle kombiniert.

Anders als die zuvor besprochenen Joins verwendet der Cross Join üblicherweise keine Vergleichsbedingung (die ON-Klausel). Wenn Tabelle A 3 Zeilen und Tabelle B 5 Zeilen hat, ergibt der Cross Join 15 Zeilen (3 × 5).

SELECT s.first_name, s.last_name, g.subject
FROM students s
CROSS JOIN grades g;

(Hinweis: In manchen SQL-Dialekten erreichen Sie dasselbe Ergebnis, indem Sie einfach beide Tabellen durch ein Komma getrennt auflisten: SELECT ... FROM students, grades;)

Da das vollständige Ergebnis 15 Zeilen hätte (3 Schüler × 5 Noten), hier eine Stichprobe, wie es aussieht:

first_namelast_namesubject
AliceAdelsonSEW
AliceAdelsonINSY
AliceAdelsonMEDT
AliceAdelsonMath
AliceAdelsonEnglish
BobBurgerSEW
BobBurgerINSY
CharlieCheckEnglish
  • Keine Logik nötig: Die Datenbank prüft nicht, ob die student_id übereinstimmt. Sie paart einfach jeden mit allem.
  • Größenexplosion: Bei großen Tabellen kann ein Cross Join schnell zu riesigen Ergebnismengen führen, die eine Anwendung zum Absturz bringen oder den Server verlangsamen können.
  • Praktischer Einsatz: Obwohl für Standardberichte selten verwendet, sind Cross Joins nützlich, um Testdaten zu erzeugen oder erschöpfende Kombinationen zu erstellen (z. B. jede mögliche “Farbe” mit jeder möglichen “Größe” für ein Bekleidungssortiment zu kombinieren).

Ein Natural Join ist ein spezieller Join-Typ, der Tabellen automatisch über alle Spalten verbindet, die in beiden Tabellen denselben Namen haben.

Bei einem NATURAL JOIN müssen Sie keine ON-Klausel angeben, weil die Datenbank die Beziehung “errät”, indem sie nach identischen Spaltenüberschriften sucht.

SELECT first_name, last_name, subject, score
FROM students
NATURAL JOIN grades;
  1. Die Datenbank betrachtet beide Tabellen (students und grades).
  2. Sie erkennt, dass beide Tabellen eine Spalte namens student_id haben.
  3. Sie führt einen Inner Join mit students.student_id = grades.student_id durch.
  4. Entscheidend ist: Sie nimmt die Join-Spalte (student_id) nur einmal ins Ergebnis auf, selbst wenn Sie SELECT * verwenden.
first_namelast_namesubjectscore
AliceAdelsonSEW1
AliceAdelsonINSY2
BobBurgerMEDT3
BobBurgerMath1

Wichtige Warnungen (warum er in der Produktion selten verwendet wird):

Abschnitt betitelt „Wichtige Warnungen (warum er in der Produktion selten verwendet wird):“

Obwohl er “sauberer” aussieht, weil der Code kürzer ist, wird er in der professionellen Softwareentwicklung aus mehreren Gründen oft vermieden:

  • Versteckte Logik: Wenn jemand eine neue Spalte zu beiden Tabellen hinzufügt (z. B. last_update) für internes Tracking, wird der NATURAL JOIN plötzlich versuchen, auch über diese Spalte zu verbinden. Das wird Ihre Abfrage wahrscheinlich kaputtmachen oder null Ergebnisse zurückgeben.
  • Spaltennamen-Konflikte: Wenn zwei Tabellen eine Spalte namens id oder description haben, die eigentlich Unterschiedliches bedeuten, versucht ein Natural Join fälschlicherweise, sie zu verknüpfen.
  • Lesbarkeit: Für einen Entwickler, der Ihren Code liest, ist nicht sofort ersichtlich, welche Spalten für die Verbindung verwendet werden, ohne das Schema beider Tabellen nachzuschlagen.

In der Praxis sollten Sie immer den expliziten INNER JOIN ... ON ... bevorzugen, um die Kontrolle über Ihre Daten zu behalten.

Ein Semi-Join unterscheidet sich etwas von den bisher besprochenen Joins. Während Standard-Joins (Inner, Left usw.) Spalten aus beiden Tabellen kombinieren, gibt ein Semi-Join Zeilen aus der linken Tabelle zurück, wenn es eine Übereinstimmung in der rechten Tabelle gibt, hängt aber keine Spalten aus der rechten Tabelle an.

Einfach gesagt: Er prüft, ob ein Datensatz einen Partner hat, “bringt den Partner aber nicht mit nach Hause”.

Sie verwenden einen Semi-Join, wenn es Ihnen nur darauf ankommt, ob eine Beziehung existiert, Sie aber die tatsächlichen Daten aus der zweiten Tabelle nicht benötigen. Er verhindert außerdem doppelte Zeilen, wenn ein Schüler mehrere Noten hat (anders als ein Inner Join).

In SQL gibt es kein Schlüsselwort SEMI JOIN. Stattdessen wird er üblicherweise mit dem EXISTS-Operator oder dem IN-Operator umgesetzt.

Beispiel - Alle Schüler finden, die mindestens eine Note haben:

SELECT *
FROM students s
WHERE EXISTS (
SELECT 1
FROM grades g
WHERE g.student_id = s.student_id
);
student_idfirst_namelast_name
1AliceAdelson
2BobBurger
  • Keine Duplikate: Obwohl Alice zwei Noten hat (SEW und INSY), erscheint sie nur einmal im Ergebnis. Bei einem INNER JOIN würde sie zweimal erscheinen.
  • Keine Noteninfo: Das Ergebnis enthält nur Spalten aus der Tabelle students. Sie können die Fächer oder die Noten nicht sehen.
  • Der “Anti-Join”: Das Gegenteil ist der Anti-Join (mit NOT EXISTS). Dieser würde nur Charlie Check zurückgeben, da er der Einzige ist, der keine Note hat.
MerkmalInner JoinSemi-Join (EXISTS)
SpaltenAus beiden TabellenNur aus der linken Tabelle
DuplikateAlice erscheint für jede NoteAlice erscheint nur einmal
ZielDaten kombinierenDaten filtern

Ein Anti-Join ist das logische Gegenteil eines Semi-Joins. Er gibt Zeilen aus der linken Tabelle nur dann zurück, wenn es keine Übereinstimmung in der rechten Tabelle gibt.

In unserem Schulkontext ist dies das perfekte Werkzeug, um “Waisen” zu finden - zum Beispiel Schüler, die noch keine Noten erhalten haben, oder Fächer, die keinem vorhandenen Schüler zugeordnet sind.

Ähnlich wie beim Semi-Join haben die meisten SQL-Dialekte kein Schlüsselwort ANTI JOIN. Er wird typischerweise mit NOT EXISTS oder NOT IN umgesetzt.

Beispiel - Schüler finden, denen keine Noten zugeordnet sind:

SELECT *
FROM students s
WHERE NOT EXISTS (
SELECT 1
FROM grades g
WHERE g.student_id = s.student_id
);
student_idfirst_namelast_name
3CharlieCheck

Warum nicht einfach ein Left Join mit einer NULL-Prüfung?

Abschnitt betitelt „Warum nicht einfach ein Left Join mit einer NULL-Prüfung?“

Sie können ein Anti-Join-Ergebnis tatsächlich auch mit einem LEFT JOIN erreichen, aber das ist oft weniger effizient und etwas schwerer zu lesen:

SELECT s.*
FROM students s
LEFT JOIN grades g ON s.student_id = g.student_id
WHERE g.grade_id IS NULL;

Vorteile der Verwendung von NOT EXISTS (Anti-Join-Logik):

  • Performance: Die Datenbank-Engine kann die Suche für einen bestimmten Schüler beenden, sobald sie eine einzige Note findet.
  • Klarheit: Sie drückt die Absicht klar aus: “Wähle Schüler, für die keine Notendatensätze existieren.”

In realen Datenbankschemata sind Informationen oft über mehr als zwei Tabellen verteilt und bilden ein Netz aus miteinander verbundenen Tabellen. Daher müssen wir mehr als zwei Tabellen auf einmal verbinden.

Die Logik bleibt jedoch dieselbe: Jeder Join erzeugt eine temporäre Ergebnismenge, die dann mit der nächsten Tabelle in der Reihenfolge verbunden wird.

Um dies zu demonstrieren, fügen wir eine dritte Tabelle hinzu, die Fächer mit Lehrkräften verknüpft.

teacher_idnamesubject
101GabrielSEW
102MaurhartINSY
103SteindlMath

Beim Verbinden mehrerer Tabellen hängen Sie einfach weitere JOIN- und ON-Klauseln an. Die Reihenfolge folgt üblicherweise der logischen Beziehung (Schüler ➡️ Note ➡️ Lehrkraft).

SELECT
s.first_name,
s.last_name,
g.subject,
g.score,
t.name AS teacher_name
FROM students s
INNER JOIN grades g ON s.student_id = g.student_id
INNER JOIN teachers t ON g.subject = t.subject;
first_namelast_namesubjectscoreteacher_name
AliceAdelsonSEW1Gabriel
AliceAdelsonINSY2Maurhart
BobBurgerMath1Steindl
  • Logischer Ablauf: Die Abfrage verbindet zunächst students mit grades über student_id. Sobald diese Verbindung hergestellt ist, verwendet sie die Spalte subject aus der Tabelle grades, um die passende Lehrkraft aus der Tabelle teachers hinzuzuziehen.
  • Der “Filter”-Effekt: Da wir für beide Verbindungen INNER JOIN verwendet haben, verschwindet jeder Datensatz, dem an irgendeiner Stelle in der Kette eine Verknüpfung fehlt. Beachten Sie, dass Bobs “MEDT”-Note weg ist, weil es in unserer Tabelle teachers keine Lehrkraft für “MEDT” gibt.
  • Join-Typen mischen: Sie können mischen und kombinieren. Sie könnten einen LEFT JOIN für die erste Verbindung verwenden (um alle Schüler zu behalten) und einen INNER JOIN für die zweite. Seien Sie jedoch vorsichtig: Ein nachfolgender INNER JOIN kann versehentlich die NULL-Zeilen herausfiltern, die ein vorheriger LEFT JOIN erzeugt hat.

In relationalen Datenbanken sind Daten typischerweise über mehrere Tabellen verteilt. Um diese Daten nutzbar zu machen, müssen sie kombiniert werden. Dieses Kapitel unterscheidet zwischen zwei grundlegenden Methoden: Mengenoperationen (basierend auf der Tabellenstruktur) und Joins (basierend auf logischen Beziehungen über Primär- und Fremdschlüssel).

Diese werden verwendet, um Tabellen zu kombinieren, die eine identische Struktur haben (vereinigungsverträglich).

  • UNION: Führt zwei Tabellen zusammen und entfernt Duplikate (z. B. eine kombinierte Liste von Projekten aus Villach und Klagenfurt).
  • INTERSECT: Gibt nur die Gemeinsamkeiten zurück (Projekte, die in beiden Städten laufen).
  • EXCEPT / MINUS: Ermittelt die Differenz (Projekte, die in Villach existieren, aber nicht in Klagenfurt).

Joins kombinieren Spalten aus verschiedenen Tabellen anhand einer bestimmten Bedingung (üblicherweise etwas wie ON s.id = g.student_id).

  • Inner Join: Der Standardfall. Nur Datensätze, die in beiden Tabellen einen passenden Partner haben, erscheinen im Ergebnis.
  • Left Join: Behält alle Datensätze aus der linken Tabelle, selbst wenn rechts keine entsprechenden Daten existieren (nützlich, um Schüler ohne Noten zu finden).
  • Right Join: Das Spiegelbild des Left Join - er behält alle Datensätze aus der rechten Tabelle.
  • Full Outer Join: Die “Alles-rein”-Variante. Sie zeigt alles aus beiden Tabellen und füllt fehlende Partner mit NULL auf.
  • Cross Join: Erzeugt ein kartesisches Produkt (jede Zeile mit jeder anderen Zeile gepaart). Vorsicht: Dies kann riesige Datenmengen erzeugen!
  • Natural Join: Verbindet Tabellen automatisch über Spalten mit identischen Namen (in der Praxis riskant wegen versteckter Logik).
  • Semi-Join & Anti-Join: Diese werden zum Filtern verwendet. Ein Semi-Join prüft, ob ein Partner existiert, ohne dessen Daten anzuzeigen. Ein Anti-Join findet gezielt Datensätze ohne Partner (z. B. Schüler, die keine einzige Note erhalten haben).

Lernergebnisse: Was Sie nach diesem Kapitel können sollten

Abschnitt betitelt „Lernergebnisse: Was Sie nach diesem Kapitel können sollten“

Nach Abschluss dieses Kapitels sollten Schüler in der Lage sein:

  • Unterscheiden: Wissen, wann eine Mengenoperation (UNION, INTERSECT, EXCEPT) gegenüber einem JOIN zu verwenden ist.
  • Anwenden: Die korrekte Syntax für INNER, LEFT und FULL JOIN sicher schreiben.
  • Analysieren: Vorhersagen, welche Datensätze für jeden Join-Typ in der Ergebnismenge erscheinen (insbesondere die Identifikation von NULL-Werten).
  • Probleme lösen: Gezielte Abfragen erstellen, um “verwaiste” Datensätze zu finden (z. B. mit LEFT JOIN und IS NULL oder NOT EXISTS).
  • Optimieren: Verstehen, warum Aliase (z. B. FROM students s) die Lesbarkeit verbessern und warum Natural Joins in professionellen Umgebungen vermieden werden.