SQL JOIN: Tabellen sicher verknüpfen
Du verknüpfst Tabellen, unterscheidest JOIN-Arten und erkennst, wann fehlende Bedingungen unerwünschte Zeilenkombinationen erzeugen.
Kurz gesagt
Ein JOIN verknüpft Zeilen aus Tabellen: entweder nach einer Bedingung oder zu allen möglichen Paaren. Die JOIN-Art legt fest, ob beim Verknüpfen mit einer Bedingung auch Zeilen ohne passenden Partner erscheinen.
Eine Abfrage fordert Daten aus Tabellen an. Hier lernst du die JOIN-Schreibweise für PostgreSQL, ein Datenbankmanagementsystem zum Speichern und Abfragen von Daten. Alle Tabellen und Daten dieser Lektion sind erfunden.
Deine Lernziele
Eine Tabellenzeile heißt auch Datensatz. Ein JOIN verbindet Zeilen aus zwei Tabellen zu Ergebniszeilen. Dabei entscheidet eine Verknüpfungsbedingung, welche Zeilen zusammenpassen. Für jedes passende Zeilenpaar entsteht eine Ergebniszeile.
In einer SQL-Abfrage wählt SELECT die Ergebnisspalten aus. FROM nennt die Ausgangstabelle, JOIN ergänzt die zweite Tabelle. Hinter ON steht die Verknüpfungsbedingung. Das Zeichen = prüft hier, ob zwei Werte gleich sind. Die verglichenen Spalten dürfen unterschiedlich heißen. Entscheidend ist, dass ihre Werte die gewünschte Zuordnung ausdrücken. Ein Code ist hier eine Kennung für einen Eintrag.
Mit AS vergibst du einen Alias, also einen Ersatznamen für eine Tabelle innerhalb der Abfrage. In s.zielcode trennt der Punkt den Tabellenalias s vom Spaltennamen zielcode. Kommas trennen ausgewählte Spalten. Das Semikolon beendet die SQL-Anweisung. SELECT * wählt alle Ergebnisspalten aus.
Ein JOIN ohne vorangestellte Typangabe bedeutet INNER JOIN. Dieser liefert die passenden Paare. Eine Ausgangszeile kann mehrere Partner haben und dann in mehreren Ergebniszeilen vorkommen. Ein JOIN setzt also keine eindeutigen Werte in den verglichenen Spalten voraus. In den folgenden Datenbeispielen schreiben wir Zeilen als Klammerpaare in der genannten Spaltenreihenfolge. Einfache Anführungszeichen kennzeichnen Textwerte.
Verknüpfungsbedingung
Eine Regel dafür, welche Zeilen aus zwei Tabellen zusammenpassen. Bei JOIN ... ON entsteht ein Treffer, wenn die Bedingung für das Zeilenpaar wahr ist.
Beispiel: Sendungen einem Depot zuordnen
- 1sendungen hat die Spalten sendung und zielcode: (41, 'A'), (42, 'B'), (43, 'A').
- 2depots hat die Spalten code und ort: ('A', 'Nord'), ('B', 'Süd').
- 3SELECT s.sendung, d.ort FROM sendungen AS s JOIN depots AS d ON s.zielcode = d.code;
- 4Die Bedingung vergleicht den Zielcode einer Sendung mit dem Code eines Depots.
- 5Das Ergebnis enthält (41, 'Nord'), (42, 'Süd') und (43, 'Nord').
Merke: Gleiche Spaltennamen sind für ON nicht nötig. Gibt es für eine Ausgangszeile mehrere passende Partner, entsteht für jeden Partner eine eigene Ergebniszeile.
Teste dich
INNER JOIN liefert für jedes passende Paar eine Ergebniszeile. Zeilen ohne Partner erscheinen dabei nicht. LEFT JOIN erhält zusätzlich jede Zeile der links genannten Tabelle, die keinen Partner hat. Die fehlenden Werte der rechten Seite werden durch NULL ergänzt. NULL kennzeichnet einen fehlenden oder unbekannten Wert und ist kein Textwert.
RIGHT JOIN erhält entsprechend die Zeilen der rechten Tabelle ohne Partner und ergänzt links NULL. FULL JOIN erhält Zeilen ohne Partner von beiden Seiten. Diese drei Arten heißen OUTER JOIN: Sie ergänzen das Ergebnis um Zeilen ohne Treffer. Das Wort OUTER darfst du weglassen, etwa bei LEFT OUTER JOIN und LEFT JOIN.
CROSS JOIN bildet jedes mögliche Paar aus den beiden Tabellen. Es gibt dabei keine Verknüpfungsbedingung. Die Zeilenzahl ist die Zeilenzahl der ersten Tabelle mal die Zeilenzahl der zweiten Tabelle. Ist eine Tabelle leer, gibt es keine Paare.
Für die folgende Übersicht enthält kisten(kiste, fachcode) die Zeilen ('K1', 'A') und ('K2', 'C'). faecher(code, platz) enthält ('A', 'Fenster') und ('B', 'Tür'). Die Abfrage lautet zunächst: SELECT k.kiste, f.platz FROM kisten AS k INNER JOIN faecher AS f ON k.fachcode = f.code; Für die anderen bedingten JOIN-Arten ersetzt du INNER durch LEFT, RIGHT oder FULL. Für alle Kombinationen lautet die Abfrage: SELECT k.kiste, f.platz FROM kisten AS k CROSS JOIN faecher AS f;
| JOIN-Art | Ergebniszeilen (kiste, platz) | Anzahl |
|---|---|---|
| INNER JOIN | ('K1', 'Fenster') | 1 |
| LEFT JOIN | ('K1', 'Fenster'), ('K2', NULL) | 2 |
| RIGHT JOIN | ('K1', 'Fenster'), (NULL, 'Tür') | 2 |
| FULL JOIN | ('K1', 'Fenster'), ('K2', NULL), (NULL, 'Tür') | 3 |
| CROSS JOIN | ('K1', 'Fenster'), ('K1', 'Tür'), ('K2', 'Fenster'), ('K2', 'Tür') | 4 |
Merke: Erhalten bedeutet nicht genau eine Ergebniszeile: Auch bei OUTER JOIN entsteht für jeden passenden Partner eine Zeile. NULL-Ergänzungen entstehen für Ausgangszeilen ohne passenden Partner.
Teste dich
USING ist eine Kurzform für Gleichheitsvergleiche zwischen gleichnamigen Spalten beider Tabellen. In USING (artikel) muss die Spalte artikel auf beiden Seiten vorkommen. Bei mehreren aufgelisteten Spalten müssen alle jeweiligen Wertepaare gleich sein. Die genannten Spalten erscheinen im gesamten JOIN-Ergebnis jeweils einmal. Bei ON bleiben dagegen die Spalten beider Seiten getrennt erhalten.
Beispiel: sortiment(artikel, menge) enthält ('R', 4) und ('S', 2). preise(artikel, euro) enthält ('R', 6) und ('T', 9). Hier bezeichnet euro den Preis in Euro. SELECT * FROM sortiment JOIN preise USING (artikel); Das Ergebnis enthält ('R', 4, 6) mit den Spalten artikel, menge und euro. Die Bedingung entspricht ON sortiment.artikel = preise.artikel. Mit dieser ON-Bedingung und SELECT * wären beide artikel-Spalten im Ergebnis enthalten.
NATURAL JOIN verwendet alle Spaltennamen, die in beiden Tabellen vorkommen, für Gleichheitsvergleiche. Diese gemeinsamen Spalten erscheinen ebenfalls jeweils einmal. Das ist riskant: Kommt später ein weiterer gemeinsamer Spaltenname hinzu, verändert sich die Bedingung. USING nennt die gewünschten Spalten ausdrücklich und schützt deshalb vor dieser unbeabsichtigten Erweiterung. Haben die Tabellen keine gemeinsamen Spaltennamen, verhält sich NATURAL JOIN wie CROSS JOIN.
Ein Self Join verknüpft eine Tabelle mit sich selbst. Dabei gibst du den beiden Vorkommen verschiedene Aliase, damit du ihre Rollen und Spalten unterscheiden kannst. Ein Self Join ist nicht auf INNER, LEFT und RIGHT JOIN beschränkt. Auch USING und NATURAL sind nicht grundsätzlich verboten. Sie können jedoch den im folgenden Beispiel benötigten Vergleich unterschiedlich benannter Spalten nicht ausdrücken.
USING
USING nennt ausdrücklich gleichnamige Spalten beider Tabellen. Ein Zeilenpaar passt, wenn die Werte in jedem genannten Spaltenpaar gleich sind.
Beispiel: Stationen mit ihrer nächsten Station verbinden
- 1stationen hat die Spalten nummer und naechste_nummer: (2, 5), (5, 9), (9, 0). Die Nummer 0 kommt in der Spalte nummer nicht vor.
- 2Die Aliase start und ziel bezeichnen zwei Rollen derselben Tabelle.
- 3SELECT start.nummer, ziel.nummer FROM stationen AS start JOIN stationen AS ziel ON start.naechste_nummer = ziel.nummer;
- 4Das Ergebnis enthält die Nummernpaare (2, 5) und (5, 9). Für die nächste Nummer 0 gibt es keinen Partner.
- 5USING (nummer) würde die beiden nummer-Spalten vergleichen. Benötigt wird hier jedoch der Vergleich von naechste_nummer mit nummer.
Merke: USING macht die ausgewählten Vergleichsspalten sichtbar. NATURAL richtet die Bedingung nach sämtlichen gemeinsamen Spaltennamen aus. Beim Self Join trennen verschiedene Aliase die Rollen der Tabelle.
Teste dich
Eine durch Kommas getrennte Tabellenliste hinter FROM bildet zunächst alle möglichen Zeilenpaare. Ohne einschränkende WHERE-Bedingung bleiben diese Paare erhalten. WHERE filtert Zeilen anhand einer Bedingung. Fehlt bei einer beabsichtigten Zuordnung die passende Bedingung, können dadurch unerwünschte Kombinationen entstehen.
Für einen INNER JOIN darf die Verknüpfungsbedingung auch in WHERE stehen. FROM a, b WHERE a.code = b.code verknüpft die Tabellen a und b über ihre code-Spalten. Das fehlende ON bedeutet hier nicht, dass die Verknüpfungsbedingung fehlt. Auch USING und NATURAL können die Bedingung festlegen.
Ein bloßes JOIN oder INNER JOIN ohne ON, USING oder NATURAL ist in der hier verwendeten PostgreSQL-Schreibweise ungültig. Es erzeugt nicht automatisch alle Paare. Ein ausdrücklicher CROSS JOIN ist dagegen gültig und bildet alle Kombinationen. Das kann gewollt sein; bei einer Zuordnungsaufgabe ist es häufig die falsche Wahl.
Bei OUTER JOIN darfst du ON und WHERE nicht einfach austauschen. Die ON-Bedingung entscheidet auch darüber, welche Ausgangszeilen keinen Partner haben und NULL-Ergänzungen erhalten. Ein nachträglicher WHERE-Filter kann solche Ergebniszeilen wieder entfernen.
Kartesisches Produkt
Die Menge aller Zeilenkombinationen aus zwei Tabellen: Jede Zeile der ersten Tabelle wird mit jeder Zeile der zweiten kombiniert. Bei drei und zwei Ausgangszeilen entstehen sechs Ergebniszeilen.
Beispiel: Fahrten ihren Fahrzeugen zuordnen
- 1fahrten(fahrt, fahrzeugcode) enthält ('F1', 'V2'), ('F2', 'V4') und ('F3', 'V2').
- 2fahrzeuge(code, typ) enthält ('V2', 'Bus') und ('V4', 'Rad').
- 3SELECT f.fahrt, z.typ FROM fahrten AS f, fahrzeuge AS z;
- 4Ohne Filter entstehen sechs Paare. Dazu gehört auch ('F1', 'Rad'), obwohl die Fahrt den Fahrzeugcode 'V2' hat.
- 5SELECT f.fahrt, z.typ FROM fahrten AS f, fahrzeuge AS z WHERE f.fahrzeugcode = z.code;
- 6Mit dieser Bedingung bleiben ('F1', 'Bus'), ('F2', 'Rad') und ('F3', 'Bus').
Merke: Prüfe die gesamte Abfrage: Eine Bedingung kann in ON, USING, NATURAL oder bei einer inneren Verknüpfung in WHERE stecken. Unterscheide eine ungültige Abfrage von einer gültigen Abfrage mit unerwünschten Kombinationen.
Teste dich
Alles auf einen Blick
SQL JOIN
ON kann auch unterschiedlich benannte Spalten vergleichen; die Bedingung muss die gewünschte Beziehung ausdrücken.
Für jedes passende Zeilenpaar entsteht eine Ergebniszeile, auch bei mehreren Partnern je Ausgangszeile.
LEFT, RIGHT und FULL JOIN ergänzen Zeilen ohne Partner auf der jeweiligen Seite um NULL-Werte.
USING nennt gemeinsame Vergleichsspalten ausdrücklich; NATURAL verwendet alle gemeinsamen Spaltennamen.
Prüfe auch WHERE, USING und NATURAL, bevor du eine fehlende Bedingung vermutest.
Alle Automaten mit ihrem Standort anzeigen
- 1automaten(geraet, standortcode) enthält ('A1', 'O2'), ('A2', 'O5') und ('A3', 'O2'). standorte(code, bezeichnung) enthält ('O2', 'Halle') und ('O7', 'Eingang').
- 2Jeder Automat soll erscheinen, auch wenn sein Standortcode keinen Partner hat. Deshalb steht automaten links und wir wählen LEFT JOIN.
- 3Die Zuordnung vergleicht a.standortcode mit s.code. Die Aliase a und s bezeichnen automaten und standorte.
- 4SELECT a.geraet, s.bezeichnung FROM automaten AS a LEFT JOIN standorte AS s ON a.standortcode = s.code;
- 5Für 'A1' und 'A3' passt jeweils der Standort 'O2'. Es entstehen ('A1', 'Halle') und ('A3', 'Halle').
- 6Für 'A2' fehlt der Standort 'O5'. LEFT JOIN ergänzt deshalb ('A2', NULL). Das Ergebnis enthält insgesamt drei Zeilen.
Fehler finden
Fehler in Zeile 2Beim bloßen JOIN fehlt ON, USING oder NATURAL. Die Abfrage ist ungültig. Richtig: SELECT * FROM baelle CROSS JOIN netze; liefert sechs Ergebniszeilen.
Lückentext
Zwei Tabellen haben je eine Zeile mit demselben Code. Die linke Tabelle hat zusätzlich eine Zeile ohne Partner. Ein LEFT JOIN über die gleichen Codes liefert Ergebniszeilen. Die Spalte „code“ kommt in beiden Tabellen vor. Soll die Verknüpfung auch nach dem Hinzufügen weiterer gemeinsamer Spaltennamen ausdrücklich nur „code“ vergleichen, wählst du . Zwei Tabellen enthalten 3 und 4 Zeilen. Eine FROM-Kommaliste ohne WHERE-Filter erzeugt Ergebniszeilen.