EMZETT.
Login

Kurz: Ein JOIN kombiniert Zeilen aus zwei Tabellen anhand einer Bedingung, meist Fremdschlüssel = Primärschlüssel. Unsere Beispieldaten: Ben und Eva haben keine Bestellung, Bestellung 7 hat keinen Kunden.

Teil des Kurses SQL

Kapitel 8 von 15 im Kurs SQL. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Tabellen verbinden

Ein JOIN kombiniert Zeilen aus zwei Tabellen anhand einer Bedingung, meist Fremdschlüssel = Primärschlüssel. Unsere Beispieldaten: Ben und Eva haben keine Bestellung, Bestellung 7 hat keinen Kunden.

INNER JOIN: nur Treffer auf beiden Seiten

CREATE TABLE kunden (id INTEGER PRIMARY KEY, name TEXT NOT NULL, ort TEXT, punkte INTEGER);
CREATE TABLE bestellungen (id INTEGER PRIMARY KEY, kunde_id INTEGER, produkt TEXT, preis REAL, menge INTEGER, datum TEXT);
INSERT INTO kunden VALUES (1,'Mia','Berlin',120),(2,'Tom','Hamburg',80),(3,'Zoe','Berlin',200),(4,'Ben','München',NULL),(5,'Eva','Köln',150);
INSERT INTO bestellungen VALUES
 (1,1,'Laptop',999.00,1,'2025-01-10'),(2,1,'Maus',25.50,2,'2025-01-12'),(3,2,'Monitor',189.90,1,'2025-02-01'),
 (4,3,'Tastatur',49.00,1,'2025-02-15'),(5,3,'Maus',25.50,1,'2025-03-03'),(6,3,'Laptop',999.00,1,'2025-03-20'),
 (7,NULL,'Kabel',5.00,3,'2025-03-21');
SELECT k.name, b.produkt, b.preis
FROM kunden AS k
INNER JOIN bestellungen AS b ON b.kunde_id = k.id
ORDER BY k.name, b.id;

Ausgabe:

name  produkt   preis
----  --------  -----
Mia   Laptop    999.0
Mia   Maus      25.5
Tom   Monitor   189.9
Zoe   Tastatur  49.0
Zoe   Maus      25.5
Zoe   Laptop    999.0

Aliase (k, b) verkürzen den Code. Bei gleichnamigen Spalten brauchst du den Tabellenpräfix.

CREATE TABLE kunden (id INTEGER PRIMARY KEY, name TEXT NOT NULL, ort TEXT, punkte INTEGER);
CREATE TABLE bestellungen (id INTEGER PRIMARY KEY, kunde_id INTEGER, produkt TEXT, preis REAL, menge INTEGER, datum TEXT);
INSERT INTO kunden VALUES (1,'Mia','Berlin',120),(2,'Tom','Hamburg',80),(3,'Zoe','Berlin',200),(4,'Ben','München',NULL),(5,'Eva','Köln',150);
INSERT INTO bestellungen VALUES
 (1,1,'Laptop',999.00,1,'2025-01-10'),(2,1,'Maus',25.50,2,'2025-01-12'),(3,2,'Monitor',189.90,1,'2025-02-01'),
 (4,3,'Tastatur',49.00,1,'2025-02-15'),(5,3,'Maus',25.50,1,'2025-03-03'),(6,3,'Laptop',999.00,1,'2025-03-20'),
 (7,NULL,'Kabel',5.00,3,'2025-03-21');
SELECT k.name, b.produkt
FROM kunden k
LEFT JOIN bestellungen b ON b.kunde_id = k.id
ORDER BY k.id, b.id;

Ausgabe:

name  produkt
----  --------
Mia   Laptop
Mia   Maus
Tom   Monitor
Zoe   Tastatur
Zoe   Maus
Zoe   Laptop
Ben
Eva

Ben und Eva bleiben erhalten, ihre Bestellspalten sind NULL. So findest du Datensätze ohne Gegenstück:

CREATE TABLE kunden (id INTEGER PRIMARY KEY, name TEXT NOT NULL, ort TEXT, punkte INTEGER);
CREATE TABLE bestellungen (id INTEGER PRIMARY KEY, kunde_id INTEGER, produkt TEXT, preis REAL, menge INTEGER, datum TEXT);
INSERT INTO kunden VALUES (1,'Mia','Berlin',120),(2,'Tom','Hamburg',80),(3,'Zoe','Berlin',200),(4,'Ben','München',NULL),(5,'Eva','Köln',150);
INSERT INTO bestellungen VALUES
 (1,1,'Laptop',999.00,1,'2025-01-10'),(2,1,'Maus',25.50,2,'2025-01-12'),(3,2,'Monitor',189.90,1,'2025-02-01'),
 (4,3,'Tastatur',49.00,1,'2025-02-15'),(5,3,'Maus',25.50,1,'2025-03-03'),(6,3,'Laptop',999.00,1,'2025-03-20'),
 (7,NULL,'Kabel',5.00,3,'2025-03-21');
SELECT k.name AS kunde_ohne_bestellung
FROM kunden k
LEFT JOIN bestellungen b ON b.kunde_id = k.id
WHERE b.id IS NULL;

Ausgabe:

kunde_ohne_bestellung
---------------------
Ben
Eva

Weitere Join-Arten

JoinErgebnis
INNER JOINnur Zeilen mit Treffer in beiden Tabellen
LEFT JOINalle links, rechts Treffer oder NULL
RIGHT JOINalle rechts (ältere SQLite-Versionen kennen es nicht; tausche einfach die Tabellen)
FULL OUTER JOINalle von beiden Seiten (SQLite ab 3.39)
CROSS JOINalle Kombinationen (Kreuzprodukt)
CREATE TABLE kunden (id INTEGER PRIMARY KEY, name TEXT NOT NULL, ort TEXT, punkte INTEGER);
CREATE TABLE bestellungen (id INTEGER PRIMARY KEY, kunde_id INTEGER, produkt TEXT, preis REAL, menge INTEGER, datum TEXT);
INSERT INTO kunden VALUES (1,'Mia','Berlin',120),(2,'Tom','Hamburg',80),(3,'Zoe','Berlin',200),(4,'Ben','München',NULL),(5,'Eva','Köln',150);
INSERT INTO bestellungen VALUES
 (1,1,'Laptop',999.00,1,'2025-01-10'),(2,1,'Maus',25.50,2,'2025-01-12'),(3,2,'Monitor',189.90,1,'2025-02-01'),
 (4,3,'Tastatur',49.00,1,'2025-02-15'),(5,3,'Maus',25.50,1,'2025-03-03'),(6,3,'Laptop',999.00,1,'2025-03-20'),
 (7,NULL,'Kabel',5.00,3,'2025-03-21');
SELECT k.name, b.id AS bestellung
FROM kunden k
FULL OUTER JOIN bestellungen b ON b.kunde_id = k.id
ORDER BY k.name, b.id;

Ausgabe:

name  bestellung
----  ----------
      7
Ben
Eva
Mia   1
Mia   2
Tom   3
Zoe   4
Zoe   5
Zoe   6
CREATE TABLE farben (f TEXT); CREATE TABLE groessen (g TEXT);
INSERT INTO farben VALUES ('rot'), ('blau');
INSERT INTO groessen VALUES ('S'), ('M'), ('L');
SELECT f, g FROM farben CROSS JOIN groessen ORDER BY f, g;

Ausgabe:

f     g
----  -
blau  L
blau  M
blau  S
rot   L
rot   M
rot   S

Joins mit Aggregation

CREATE TABLE kunden (id INTEGER PRIMARY KEY, name TEXT NOT NULL, ort TEXT, punkte INTEGER);
CREATE TABLE bestellungen (id INTEGER PRIMARY KEY, kunde_id INTEGER, produkt TEXT, preis REAL, menge INTEGER, datum TEXT);
INSERT INTO kunden VALUES (1,'Mia','Berlin',120),(2,'Tom','Hamburg',80),(3,'Zoe','Berlin',200),(4,'Ben','München',NULL),(5,'Eva','Köln',150);
INSERT INTO bestellungen VALUES
 (1,1,'Laptop',999.00,1,'2025-01-10'),(2,1,'Maus',25.50,2,'2025-01-12'),(3,2,'Monitor',189.90,1,'2025-02-01'),
 (4,3,'Tastatur',49.00,1,'2025-02-15'),(5,3,'Maus',25.50,1,'2025-03-03'),(6,3,'Laptop',999.00,1,'2025-03-20'),
 (7,NULL,'Kabel',5.00,3,'2025-03-21');
SELECT k.name, count(b.id) AS bestellungen, COALESCE(sum(b.preis * b.menge), 0) AS umsatz
FROM kunden k
LEFT JOIN bestellungen b ON b.kunde_id = k.id
GROUP BY k.id
ORDER BY umsatz DESC;

Ausgabe:

name  bestellungen  umsatz
----  ------------  ------
Zoe   3             1073.5
Mia   2             1050.0
Tom   1             189.9
Ben   0             0
Eva   0             0

Tipp

count(b.id) statt count(*) verwenden, sonst zählt ein Kunde ohne Bestellung als 1.

Mehr als zwei Tabellen

CREATE TABLE studenten (id INTEGER PRIMARY KEY, name TEXT);
CREATE TABLE kurse (id INTEGER PRIMARY KEY, titel TEXT);
CREATE TABLE belegungen (student_id INTEGER, kurs_id INTEGER, note REAL);
INSERT INTO studenten VALUES (1,'Mia'),(2,'Tom');
INSERT INTO kurse VALUES (1,'SQL'),(2,'Python');
INSERT INTO belegungen VALUES (1,1,1.3),(1,2,2.0),(2,1,2.7);
 
SELECT s.name, k.titel, b.note
FROM belegungen b
JOIN studenten s ON s.id = b.student_id
JOIN kurse k ON k.id = b.kurs_id
ORDER BY s.name, k.titel;

Ausgabe:

name  titel   note
----  ------  ----
Mia   Python  2.0
Mia   SQL     1.3
Tom   SQL     2.7

Self Join

Eine Tabelle mit sich selbst verbinden, z. B. Mitarbeiter und Vorgesetzte:

CREATE TABLE mitarbeiter (id INTEGER PRIMARY KEY, name TEXT, chef_id INTEGER);
INSERT INTO mitarbeiter VALUES (1,'Anna',NULL),(2,'Ben',1),(3,'Cem',1),(4,'Dina',2);
SELECT m.name AS mitarbeiter, c.name AS chef
FROM mitarbeiter m
LEFT JOIN mitarbeiter c ON c.id = m.chef_id
ORDER BY m.id;

Ausgabe:

mitarbeiter  chef
-----------  ----
Anna
Ben          Anna
Cem          Anna
Dina         Ben

Merke

  • INNER JOIN: nur Treffer; LEFT JOIN: alle links; FULL/CROSS für Sonderfälle
  • Bedingung mit ON, Aliase für Lesbarkeit
  • LEFT JOIN + WHERE rechts.id IS NULL findet fehlende Gegenstücke
  • Bei Zählungen über LEFT JOIN count(spalte_rechts) verwenden
  • Eine Tabelle kann mit sich selbst verbunden werden

Übungsaufgabe

Liste alle Produkte mit dem Namen des Kunden, der sie bestellt hat, und markiere Bestellungen ohne Kunden als “unbekannt”.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Beziehungen und Fremdschlüssel

Weiter: Unterabfragen, CTEs und Mengenoperationen

Alle Kapitel: SQL im Überblick