In relationalen Datenbanken sind Daten oft auf mehrere Tabellen verteilt, um Redundanz zu vermeiden und die Datenintegrität zu gewährleisten. Um die Daten sinnvoll auswerten zu können, müssen die Tabellen miteinander verknüpft werden. Hierfür gibt es in SQL die JOIN-Anweisung.
Die Verbindung von Tabellen basiert häufig auf Primär-Fremdschlüssel-Beziehungen. Ein Primärschlüssel ist ein eindeutiger Identifikator für jeden Datensatz in einer Tabelle. Ein Fremdschlüssel in einer anderen Tabelle verweist auf diesen Primärschlüssel und stellt so eine Beziehung zwischen den beiden Tabellen her.
Hier ein typisches Beispiel:
-- Primärschlüsseltabelle
CREATE TABLE abteilungen (
abteilung_id INT PRIMARY KEY,
name VARCHAR(50)
);
-- Fremdschlüsseltabelle
CREATE TABLE mitarbeiter (
mitarbeiter_id INT PRIMARY KEY,
name VARCHAR(100),
abteilung_id INT,
FOREIGN KEY (abteilung_id)
REFERENCES abteilungen(abteilung_id)
);In diesem Beispiel ist abteilung_id der Primärschlüssel
in der Tabelle abteilungen. Die Tabelle
mitarbeiter enthält eine Fremdschlüsselspalte
abteilung_id, die auf den Primärschlüssel in
abteilungen verweist. Dadurch wird eine Beziehung zwischen
Mitarbeitern und Abteilungen hergestellt.
Der INNER JOIN verbindet nur Datensätze, die in beiden Tabellen vorhanden sind. Das bedeutet, nur Mitarbeiter, die einer Abteilung zugeordnet sind und nur Abteilungen, denen Mitarbeiter zugeordnet sind, werden im Ergebnis angezeigt.
-- Grundlegende Syntax
SELECT m.name, a.name AS abteilung
FROM mitarbeiter m
INNER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id;
-- Mit weiteren Bedingungen
SELECT m.name, a.name AS abteilung
FROM mitarbeiter m
INNER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id
WHERE a.name = 'Vertrieb';
-- Mit Sortierung
SELECT m.name, a.name AS abteilung
FROM mitarbeiter m
INNER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id
ORDER BY a.name, m.name;Der INNER JOIN wird mit der Klausel ON definiert, die
angibt, welche Spalten für die Verknüpfung verwendet werden sollen. In
diesem Fall wird der Fremdschlüssel abteilung_id aus der
Tabelle mitarbeiter mit dem Primärschlüssel
abteilung_id aus der Tabelle abteilungen
verknüpft.
Es gibt verschiedene Möglichkeiten, JOINs zu schreiben. Die Standardschreibweise mit explizitem INNER JOIN und ON-Klausel ist am übersichtlichsten und wird daher empfohlen.
-- Standardschreibweise (empfohlen)
SELECT m.name, a.name
FROM mitarbeiter m
INNER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id;
-- Verkürzt mit USING (nur bei gleichen Spaltennamen)
SELECT m.name, a.name
FROM mitarbeiter m
INNER JOIN abteilungen a
USING (abteilung_id);
-- Alte Schreibweise (nicht empfohlen)
SELECT m.name, a.name
FROM mitarbeiter m, abteilungen a
WHERE m.abteilung_id = a.abteilung_id;Die verkürzte Schreibweise mit USING kann verwendet
werden, wenn die Spaltennamen in beiden Tabellen identisch sind. Die
alte Schreibweise ohne expliziten JOIN ist weniger übersichtlich und
gilt als veraltet.
JOINs können auch für temporäre Tabellen verwendet werden. Eine temporäre Tabelle ist eine Tabelle, die nur für die Dauer einer Sitzung oder Transaktion existiert. Sie kann verwendet werden, um Zwischenergebnisse zu speichern oder komplexe Abfragen zu vereinfachen.
-- Temporäre Tabelle aus JOIN erstellen
CREATE TEMPORARY TABLE mitarbeiter_details
SELECT
m.mitarbeiter_id,
m.name,
a.name AS abteilung,
g.bezeichnung AS gehaltsstufe
FROM mitarbeiter m
INNER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id
INNER JOIN gehaltsstufen g
ON m.gehaltsstufe_id = g.gehaltsstufe_id;In diesem Beispiel wird eine temporäre Tabelle
mitarbeiter_details erstellt, die Daten aus den Tabellen
mitarbeiter, abteilungen und
gehaltsstufen kombiniert. Die temporäre Tabelle kann dann
für weitere Abfragen verwendet werden.
JOINs können auch über andere Spalten als Primär- und Fremdschlüssel erfolgen. Beispielsweise können Kunden anhand ihrer Postleitzahl verknüpft werden oder Bestellungen anhand eines Datums mit Aktionen.
-- JOIN über Namen
SELECT k1.name, k2.name AS partner
FROM kunden k1
INNER JOIN kunden k2
ON k1.plz = k2.plz
AND k1.kunde_id < k2.kunde_id;
-- JOIN über Datumsbereich
SELECT b.bestellnummer, p.aktion
FROM bestellungen b
INNER JOIN promotion p
ON b.datum BETWEEN p.start_datum AND p.end_datum;Im ersten Beispiel werden Kunden mit gleicher Postleitzahl verknüpft,
wobei jeder Kunde nur einmal als “partner” auftauchen soll (daher die
zusätzliche Bedingung k1.kunde_id < k2.kunde_id).
Im zweiten Beispiel werden Bestellungen mit Aktionen verknüpft, die
zu diesem Zeitpunkt gültig waren. Die Bedingung
b.datum BETWEEN p.start_datum AND p.end_datum prüft, ob das
Bestelldatum innerhalb des Gültigkeitszeitraums der Aktion liegt.
Komplexere Abfragen können mehrere Tabellen verbinden. Dabei werden einfach mehrere JOIN-Anweisungen hintereinander geschrieben.
-- Drei Tabellen verbinden
SELECT
b.bestellnummer,
k.name AS kunde,
p.bezeichnung AS produkt
FROM bestellungen b
INNER JOIN kunden k
ON b.kunde_id = k.kunde_id
INNER JOIN bestellpositionen bp
ON b.bestell_id = bp.bestell_id
INNER JOIN produkte p
ON bp.produkt_id = p.produkt_id;
-- Mit Aggregation
SELECT
k.name AS kunde,
a.name AS abteilung,
COUNT(*) AS anzahl_bestellungen,
SUM(b.betrag) AS gesamtumsatz
FROM bestellungen b
INNER JOIN kunden k
ON b.kunde_id = k.kunde_id
INNER JOIN abteilungen a
ON k.abteilung_id = a.abteilung_id
GROUP BY k.kunde_id, a.abteilung_id;Im ersten Beispiel werden die Tabellen bestellungen,
kunden, bestellpositionen und
produkte verknüpft, um für jede Bestellung den Kundennamen
und das bestellte Produkt anzuzeigen.
Im zweiten Beispiel werden Bestellungen nach Kunde und Abteilung gruppiert und aggregiert. Für jeden Kunden wird die Anzahl der Bestellungen und der Gesamtumsatz berechnet.
Im Gegensatz zum INNER JOIN, der nur Datensätze anzeigt, die in beiden Tabellen vorhanden sind, zeigt der OUTER JOIN auch Datensätze an, die in einer der beiden Tabellen keine Entsprechung haben. Es gibt zwei Arten von OUTER JOINs:
-- LEFT OUTER JOIN
SELECT m.name, a.name AS abteilung
FROM mitarbeiter m
LEFT OUTER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id;
-- RIGHT OUTER JOIN
SELECT m.name, a.name AS abteilung
FROM mitarbeiter m
RIGHT OUTER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id;
-- Fehlende Zuordnungen finden
SELECT m.name, a.name AS abteilung
FROM mitarbeiter m
LEFT OUTER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id
WHERE a.abteilung_id IS NULL;Im ersten Beispiel werden alle Mitarbeiter angezeigt, auch wenn sie keiner Abteilung zugeordnet sind (die Abteilung ist dann NULL).
Im zweiten Beispiel werden alle Abteilungen angezeigt, auch wenn ihnen keine Mitarbeiter zugeordnet sind (der Mitarbeitername ist dann NULL).
Im dritten Beispiel werden mit Hilfe eines LEFT OUTER JOIN und einer zusätzlichen WHERE-Bedingung alle Mitarbeiter gesucht, die keiner Abteilung zugeordnet sind.
Eine Tabelle kann auch mit sich selbst verbunden werden. Dies wird als SELF JOIN bezeichnet und ist nützlich, um Beziehungen innerhalb einer Tabelle darzustellen, wie z.B. hierarchische Strukturen.
-- Mitarbeiter und ihre Vorgesetzten
SELECT
m1.name AS mitarbeiter,
m2.name AS vorgesetzter
FROM mitarbeiter m1
LEFT JOIN mitarbeiter m2
ON m1.vorgesetzter_id = m2.mitarbeiter_id;
-- Hierarchische Strukturen
SELECT
k1.kategorie AS hauptkategorie,
k2.kategorie AS unterkategorie
FROM kategorien k1
LEFT JOIN kategorien k2
ON k2.parent_id = k1.kategorie_id
WHERE k1.parent_id IS NULL;Im ersten Beispiel wird die Tabelle mitarbeiter mit sich
selbst verknüpft, um für jeden Mitarbeiter seinen Vorgesetzten
anzuzeigen. Der LEFT JOIN stellt sicher, dass auch Mitarbeiter angezeigt
werden, die keinen Vorgesetzten haben (der Vorgesetzte ist dann
NULL).
Im zweiten Beispiel wird eine hierarchische Struktur von Kategorien
abgebildet. Jede Kategorie kann eine Oberkategorie
(parent_id) haben. Der SELF JOIN verbindet jede Kategorie
mit ihrer Oberkategorie. Die WHERE-Bedingung filtert nur die obersten
Kategorien (die keine Oberkategorie haben).
JOINs können bei großen Tabellen zu Performanceproblemen führen. Hier sind einige Tipps zur Optimierung:
-- Indizes für JOIN-Spalten
CREATE INDEX idx_abteilung
ON mitarbeiter(abteilung_id);
-- Zusammengesetzter Index für mehrere Bedingungen
CREATE INDEX idx_bestellung
ON bestellungen(kunde_id, datum);
-- Temporäre Tabellen für Zwischenergebnisse
CREATE TEMPORARY TABLE temp_umsatz AS
SELECT
kunde_id,
SUM(betrag) AS gesamtumsatz
FROM bestellungen
GROUP BY kunde_id;Praktische Tipps für JOINs:
-- Gut
SELECT m.name, a.name AS abteilung
FROM mitarbeiter m
INNER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id;Aliase wie m für mitarbeiter und
a für abteilungen machen die Abfrage
übersichtlicher. Verwenden Sie immer Aliase, insbesondere bei mehreren
Tabellen.
-- Besser: Direkte Abfrage wenn möglich
SELECT name FROM mitarbeiter
WHERE abteilung_id = 5;
-- Statt
SELECT m.name
FROM mitarbeiter m
INNER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id
WHERE a.abteilung_id = 5;Wenn Sie nur Daten aus einer Tabelle benötigen und die Bedingung direkt auf diese Tabelle angewendet werden kann, ist ein JOIN überflüssig und verlangsamt nur die Abfrage.
EXPLAIN SELECT m.name, a.name
FROM mitarbeiter m
INNER JOIN abteilungen a
ON m.abteilung_id = a.abteilung_id;Mit EXPLAIN können Sie sich den Ausführungsplan einer
Abfrage anzeigen lassen. So können Sie sehen, wie die Tabellen verknüpft
werden und ob Indizes verwendet werden. Dies hilft bei der
Optimierung.
-- Kleine Tabelle zuerst
SELECT m.name, a.name
FROM abteilungen a
INNER JOIN mitarbeiter m
ON m.abteilung_id = a.abteilung_id;Wenn eine der zu verknüpfenden Tabellen deutlich kleiner ist als die andere, sollte diese zuerst in der FROM-Klausel stehen. Die Datenbank optimiert die Abfrage, indem sie für jeden Datensatz in der kleinen Tabelle die passenden Datensätze in der großen Tabelle sucht. Dadurch werden insgesamt weniger Datensätze durchsucht.
JOINs sind ein mächtiges Werkzeug, um Daten aus verschiedenen Tabellen zu kombinieren. Sie ermöglichen es, Beziehungen zwischen Tabellen auszunutzen und komplexe Abfragen zu formulieren. Gleichzeitig erfordern sie aber auch ein gutes Verständnis der Tabellenstruktur und der Zusammenhänge zwischen den Tabellen.
Hier noch einmal die wichtigsten Punkte zusammengefasst:
Mit einem guten Verständnis von JOINs können Sie die volle Leistungsfähigkeit relationaler Datenbanken ausschöpfen und auch komplexe Datenabfragen effizient gestalten. Üben Sie die verschiedenen JOIN-Typen anhand praktischer Beispiele und analysieren Sie bestehende Abfragen, um ein Gefühl für die Möglichkeiten und Grenzen von JOINs zu bekommen.