Ein MSSQL JOIN verbindet Zeilen aus zwei Tabellen anhand einer Bedingung. Damit kannst du etwa Bestellungen zusammen mit Kundennamen anzeigen. Entscheidend ist die JOIN-Art: Sollen nur passende Zeilen erscheinen oder auch Kunden ohne Bestellung? In dieser Anleitung lernst du INNER, LEFT, RIGHT, FULL und CROSS JOIN an denselben Beispieldaten kennen. Danach siehst du, wie Filter und mehrfach passende Zeilen das Ergebnis verändern.
Was ist ein MSSQL JOIN?
„MSSQL“ steht hier für Microsoft SQL Server; seine Abfragesprache heißt Transact-SQL (T-SQL). Ein JOIN beschreibt, wie Zeilen aus zwei Datenquellen miteinander kombiniert werden. Meist vergleichst du dazu einen Fremdschlüssel mit einem eindeutigen Schlüssel der anderen Tabelle, zum Beispiel Bestellungen.KundeID = Kunden.KundeID. Die JOIN-Bedingung steht bei den üblichen JOIN-Arten hinter ON.
Ein JOIN fasst Daten nicht automatisch zu einer Zeile pro Kunde zusammen. Hat ein Kunde zwei passende Bestellungen, erscheinen bei einem einfachen JOIN zwei Ergebniszeilen. Welche Zeilen ohne Partner erhalten bleiben, bestimmt die JOIN-Art. Microsoft beschreibt die unterstützten Arten in der T-SQL-Referenz zur FROM-Klausel.
Testdaten: Kunden und Bestellungen
Die folgenden Abfragen verwenden zwei temporäre Tabellen. Führe den Block in SQL Server Management Studio in einem Abfragefenster aus und verwende für die weiteren Beispiele dieselbe Verbindung. Die Demo verändert keine vorhandenen Geschäftstabellen. Bei erneutem Ausführen werden nur diese beiden temporären Tabellen der Sitzung neu erstellt.
DROP TABLE IF EXISTS #JoinBestellungen;
DROP TABLE IF EXISTS #JoinKunden;
CREATE TABLE #JoinKunden (
KundeID int NOT NULL PRIMARY KEY,
Kundenname nvarchar(50) NOT NULL
);
CREATE TABLE #JoinBestellungen (
BestellungID int NOT NULL PRIMARY KEY,
KundeID int NULL,
Status nvarchar(20) NOT NULL,
Betrag decimal(10, 2) NOT NULL
);
INSERT INTO #JoinKunden (KundeID, Kundenname)
VALUES (1, N'Anna'),
(2, N'Ben'),
(3, N'Cara'),
(4, N'Doro');
INSERT INTO #JoinBestellungen
(BestellungID, KundeID, Status, Betrag)
VALUES (101, 1, N'bezahlt', 120.00),
(102, 1, N'offen', 80.00),
(103, 2, N'offen', 50.00),
(104, 99, N'bezahlt', 60.00);
Anna hat zwei Bestellungen, Ben eine, Cara und Doro keine. Bestellung 104 verweist bewusst auf die nicht vorhandene KundeID 99. Nur für diese Demonstration gibt es deshalb keinen Fremdschlüssel zwischen den Tabellen. In einer echten Datenbank sollte ein passender Fremdschlüssel solche verwaisten Verweise verhindern. DROP TABLE IF EXISTS benötigt SQL Server 2016 oder neuer.
JOIN-Arten auf einen Blick
| JOIN-Art | Welche Zeilen bleiben? | Zeilen im Demo-Ergebnis |
|---|---|---|
INNER JOIN |
Nur passende Paare | 3 |
LEFT JOIN |
Alle Kunden links, dazu passende Bestellungen | 5 |
RIGHT JOIN |
Alle Bestellungen rechts, dazu passende Kunden | 4 |
FULL JOIN |
Alle passenden und alle unpassenden Zeilen beider Seiten | 6 |
CROSS JOIN |
Jede Kundenzeile mit jeder Bestellungszeile | 16 |
Die Zahlen ergeben sich aus den vier Kunden und vier Bestellungen. Das Ergebnis eines JOIN ist nicht zwangsläufig so groß wie eine der Eingangstabellen: Annas zwei Bestellungen erzeugen zwei passende Zeilen.
INNER JOIN: nur Zeilen mit passendem Partner
INNER JOIN liefert jedes passende Paar aus Kunde und Bestellung. Kunden ohne Bestellung sowie die verwaiste Bestellung 104 fehlen:
SELECT k.KundeID, k.Kundenname,
b.BestellungID, b.Betrag
FROM #JoinKunden AS k
INNER JOIN #JoinBestellungen AS b
ON b.KundeID = k.KundeID
ORDER BY k.KundeID, b.BestellungID;
Ergebnis: Anna mit 101 und 102, Ben mit 103. Das sind drei Zeilen, obwohl nur zwei Kunden beteiligt sind. Wenn du lediglich wissen willst, welche Kunden mindestens eine Bestellung haben, ist später gezeigtes EXISTS oft verständlicher als ein JOIN mit anschließendem DISTINCT.
LEFT JOIN: alle Kunden anzeigen
LEFT JOIN erhält jede Zeile der linken Tabelle, hier alle vier Kunden. Nicht passende Spalten der rechten Tabelle werden im Ergebnis mit NULL aufgefüllt:
SELECT k.KundeID, k.Kundenname,
b.BestellungID
FROM #JoinKunden AS k
LEFT JOIN #JoinBestellungen AS b
ON b.KundeID = k.KundeID
ORDER BY k.KundeID, b.BestellungID;
Anna erscheint zweimal, Ben einmal, Cara und Doro jeweils einmal mit NULL in BestellungID. Insgesamt sind das fünf Zeilen. Die verwaiste Bestellung 104 steht auf der rechten Seite und erscheint nicht.
Für eine Liste der Kunden ohne Bestellung prüfst du eine nicht nullable Schlüsselspalte der rechten Tabelle:
SELECT k.KundeID, k.Kundenname
FROM #JoinKunden AS k
LEFT JOIN #JoinBestellungen AS b
ON b.KundeID = k.KundeID
WHERE b.BestellungID IS NULL
ORDER BY k.KundeID;
Das liefert Cara und Doro. Eine fachlich nullable Spalte wie b.KundeID oder b.Status ist für diese Prüfung weniger eindeutig. Mehr zu Schlüsseln und Beziehungen findest du im Artikel über Primary Keys und Indizes im SQL Server.
RIGHT JOIN: alle Bestellungen erhalten
Ein RIGHT JOIN erhält alle Zeilen der rechten Tabelle. In unserem Beispiel erscheinen die Bestellungen 101 bis 104. Bei 104 bleibt der Kundenname NULL:
SELECT k.Kundenname, b.BestellungID, b.KundeID
FROM #JoinKunden AS k
RIGHT JOIN #JoinBestellungen AS b
ON b.KundeID = k.KundeID
ORDER BY b.BestellungID;
Du kannst dieselbe Leserichtung oft leichter mit einem LEFT JOIN ausdrücken, indem du die Tabellen vertauschst. RIGHT JOIN ist kein anderer Abgleichsmechanismus; es ändert, welche Eingabeseite vollständig erhalten bleibt.
FULL JOIN: Treffer und fehlende Zuordnungen beider Seiten
FULL JOIN kombiniert die passenden Paare und erhält zusätzlich nicht passende Zeilen beider Seiten:
SELECT COALESCE(k.KundeID, b.KundeID) AS KundeID,
k.Kundenname,
b.BestellungID
FROM #JoinKunden AS k
FULL JOIN #JoinBestellungen AS b
ON b.KundeID = k.KundeID
ORDER BY COALESCE(k.KundeID, b.KundeID), b.BestellungID;