Teil von SELFHTML aktuell Teil von Artikel Teil von Datenbanken

Einführung in Joins

nach unten Rouven Thimm
nach unten Normalisierung & Vorstellung des Beispiels
nach unten Joins allgemein
nach unten INNER JOIN
nach unten LEFT/RIGHT JOIN
nach unten FULL OUTER JOIN
nach unten Abschließende Anmerkungen

Rouven Thimm

E-Mail: E-Mail thimmsd@gmx.de

Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!

nach obennach unten

Normalisierung & Vorstellung des Beispiels

Bevor Joins und Join-Arten genauer vorgestellt werden, soll zunächst das Problem vorgestellt bzw. eine Motivation für die künstliche Herbeiführung des Problems gegeben werden:
Frage: Warum liegen nicht alle Datensätze in einer Tabelle?
Antwort: Weil eine Normalisierung vorgenommen wurde.

Die Normalisierung ist ein Prozess der Umgestaltung von Datenbankstrukturen in der relationalen Welt. Ziel ist die Vermeidung von zahlreichen Problemen, die sich aus der einfachen Speicherung "in einer großen Tabelle" ergeben. Das Ergebnis der Normalisierung sind zahlreiche, kleinere Relationen, in denen weniger bis keine Abhängigkeiten zwischen einzelnen Feldern bestehen und Redundanzen nicht mehr auftreten.

Beispiel: nicht normalisierte Tabelle

Nachfolgend soll ein fiktiver Teil einer Unternehmenssoftware betrachtet werden. Das Unternehmen besitzt bereits eine Kundendatei, in der zu jedem Kunden eine eindeutige Kundennummer festgehalten ist. Die zu erstellende Datenlandschaft soll folgende Sachverhalte abdecken:

  1. Kunden kaufen bei dem Unternehmen verschiedene Waren ein. Der Gesamtrechnungsbetrag soll unter einer Rechnungsnummer zusammen mit der Kundennummer abgelegt werden.
  2. Falls ein Kunde die Rechnung per Kreditkarte begleicht, so sind die nötigen Kartendaten wie Nummer, Firma, Inhaber und Ablaufdatum festzuhalten. Für bar-beglichene Rechnungen entfallen diese Angaben.
  3. Um dem Kunden bei einem erneuten Einkauf die Eingabe der Kartendaten zu ersparen müssen sämtliche bekannten Kreditkarteninformationen archiviert werden.
  4. Die Rechnungstabelle wird monatsweise geführt, d.h. sie enthält immer nur die Rechnungen eines Monats.
Rechnungen_Oktober
RechnungsNr KundenNr Betrag Kartennummer Firma Inhaber Ablaufdatum
98765 ABX039 49,95 12345 VISA Max Mustermann 05/2007
98766 ABX039 12,95 - - - -
- - - 12346 Mastercard Katrin Musterfrau 01/2008
98767 ABX040 79,95 12347 American Express John Doe 01/2007
98768 ABX050 59,99 12347 American Express John Doe 01/2007
98769 ABX050 29,99 12348 Diners Club John Doe 03/2008
98770 ABX060 99,99 - - - -

Erläuterung:

Die vorstehende Tabelle erfüllt die obigen Anforderungen. Sie ist allerdings zwangsläufig suboptimal, da sie sich mit zwei Sachverhalten gleichzeitig befasst. Zum einen hält sie fest, welcher Kunde unter welcher Rechnungsnummer welchen Betrag beglichen hat. Zum anderen speichert sie sämtliche Kreditkarteninformationen, die dem Unternehmen vorliegen. Rein logisch liegt zwischen beiden Informationen zunächst keine Beziehung vor.
Woran ist der Sachverhalt zu erkennen?
Man betrachte die Rechnung mit der Nummer '98766'. Keines der Felder 'Kartennummer', 'Firma', 'Inhaber' oder 'Ablaufdatum' ist befüllt (sog. NULL-Werte). In diesem Szenario könnte dies z.B. bedeuten, dass die entsprechende Rechnung noch nicht oder bar beglichen wurde. Betrachtet man weiterhin die Kartennummer '12346', so zeigt sich die umgekehrte Situation: Es liegen keine Rechnungsinformationen vor.

Probleme dieses Ansatzes:

Der unerfahrene Designer mag diese Tabelle ansehen und sich fragen, wo denn nun das eigentliche Problem liegt, denn offensichtlich werden ja alle Daten geliefert die das Unternehmen in diesem Zusammenhang benötigt. Der Hauptfeind dieses Modells sind Änderungen im Laufe der Zeit.

All dies ist hinfällig, wenn die Datenbankstruktur nur leicht angepasst wird.

Beispiel: normalisierte Tabelle

Um den zuvor genannten Problemen aus dem Weg zu gehen, sollte eine Normalisierung der Daten angestrebt werden. Eine mögliche Normalisierung könnte etwa wie folgt aussehen:

Kreditkarte
Kartennummer Firma Inhaber Ablaufdatum
12345 VISA Max Mustermann 05/2007
12346 Mastercard Katrin Musterfrau 01/2008
12347 American Express John Doe 01/2007
12348 Diners Club John Doe 03/2008
Rechnungen_Oktober
RechnungsNr KundenNr Betrag Kartennummer
98765 ABX039 49,95 12345
98766 ABX039 12,95 -
98767 ABX040 79,95 12347
98768 ABX050 59,99 12347
98769 ABX050 29,99 12348
98770 ABX060 99,99 -

Erläuterung:

In der nun vorliegenden Struktur wurde für jeden Sachverhalt eine eigene Tabelle gebildet. Über das eindeutige Feld (sog. 'Primärschlüssel'), in diesem Fall 'Kartennummer' bzw. 'RechnungsNr', kann ein Datensatz identifiziert werden. Zwischen der Tabelle 'Rechnungen' und der Tabelle 'Kreditkarte' besteht eine sog. 'Fremdschlüsselbeziehung': Durch den Eintrag der Kartennummer kann einer Rechnung eine Kreditkarteninformation zugeordnet werden. Keiner der Einträge ist redundant, die zuvor genannten Anomalien sind beseitigt.

Gibt es Nachteile dieser Speicherform?

Nachteile ergeben sich sowohl für einen menschlichen Leser als auch für die maschinelle Verarbeitung der Daten. Das menschliche Gehirn ist in der Lage ohne weiteres relevante Informationen wahrzunehmen und irrelevantes auszublenden. Möchte der Mensch also alle Rechnungen sehen, so ist es ihm einigermaßen egal, ob er dabei einen Satz übergehen muss oder nicht. Erst bei zahlreichen NULL-Werten in der Ausgangstabelle wäre es ggf. umständlicher diese zu lesen. Bei den Kreditkarten verhält es sich schon anders, hier ist die normalisierte Tabelle in jedem Fall zugänglicher. Um in der Urtabelle herauszufinden welche Karten bekannt sind, muss mühsam jede Rechnung betrachtet werden, nun existieren alle Karten getrennt. Knackpunkt bei der Umstrukturierung bleibt allerdings die reine Kernfunktionalität 'Welche Rechnung wurde mit welcher Kreditkarte beglichen?'. In der Ausgangslage musste hierzu lediglich 'der Datensatz' angeschaut werden. Nach der Umstrukturierung muss (1) die Rechnung angeschaut, (2) die Kartennummer ermittelt und (3) die Karteninformation an anderer Stelle nachgeschlagen werden.

Was sich hier noch als Problem der menschlichen Lesbarkeit darstellt, birgt auch für die maschinelle Verarbeitung ein Problem: Zwar können alle Informationen wiederhergestellt werden, aber der Suchaufwand ist ungleich höher. Aus einer einzigen Abfrage ('Gib mir alle Rechnungen mit Zahlungsinformation') sind entweder 2 Abfragen geworden ('Gib mir alle Rechnungen, die mit Karte gezahlt wurden' + 'Gib mir zu jeder dieser Kartennummern die Karteninformationen'), oder die Tabellen müssen wieder in ihre Ausgangsform zusammengefügt werden. Dieser JOIN kann bei großen Tabellen sehr aufwändig werden. Bei zwei Tabellen ist das Problem noch nicht unmittelbar vorhanden, stellt man sich aber komplexere Beziehungen vor, so kann die Operationen relativ viel Zeit beanspruchen. Bei großen Datenbanken wird daher nicht immer bis zur letzten Stufe normalisiert, sondern einzelne Anomalien akzeptiert und durch Programmlogik oder andere Datenbankmechanismen ausgeglichen.

Im Netz finden sich zahlreiche Seiten, die sich mit der Normalisierung von Tabellen befassen. Da dies nicht der eigentliche Inhalt dieses Artikels ist, sei dem interessierten Leser die Recherche selbst nahegelegt. Ein guter Startpunkt ist der deutschsprachige Seite Wikipedia-Artikel zur Normalisierung.

nach obennach unten

Joins allgemein

Ein Join (zu Deutsch: Verbund oder Verbindung) dient der Zusammenführung von zwei Tabellen unter bestimmten Kriterien. Durch verschiedene Arten von Joins werden dabei zusätzlich zu den eigentlichen Kriterien noch Grundregeln für die Ergebnismenge festgelegt.

Joins basieren, wie relationale Datenbanksysteme insgesamt, auf dem Konstrukt der Relation in der Mathematik. In der Theorie existieren daher eine ganze Reihe von Joins, die im Datenbankalltag keine Rolle spielen. Um langsam an das Thema heranzuführen, sollen Sie hier trotzdem genannt werden.

Join als Kreuzprodukt:

In der allereinfachsten Form des Joins werden sämtliche Datensätze der ersten Tabelle mit sämtlichen Datensätzen der zweiten Tabelle zusammengeführt.

X
A B
1 2
2 3
3 4
Y
A B C
1 2 3
2 3 4
3 4 5
X, Y
A B A B C
1 2 1 2 3
1 2 2 3 4
1 2 3 4 5
2 3 1 2 3
2 3 2 3 4
2 3 3 4 5
3 4 1 2 3
3 4 2 3 4
3 4 3 4 5

Für den praktischen Datenbankeinsatz ist diese Reinform in der Regel unbrauchbar, da keinerlei Beziehungen zwischen den Daten beachtet werden.

Equi-Join:

Während beim Kreuzprodukt keinerlei Anforderungen an die Kombination der Datensätze gestellt werden, führt der Equi-Join eine solche ein: Die Gleichheit von zwei Spalten.

X
A B
1 2
2 3
3 4
Y
A B C
1 2 3
2 3 4
3 4 5
X, Y mit A=A und B=B
A B A B C
1 2 1 2 3
2 3 2 3 4
3 4 3 4 5

Im Gegensatz zur ersten Variante sind hier also nur noch die Datensätze in der Ergebnismenge, die das Kriterium der Gleichheit erfüllt haben.

Natural Join:

Ein Natural Join ist eine Kombination von zwei Tabellen, in denen Spalten gleichen Namens existieren. Die Werte in diesen Spalten werden sodann auf Übereinstimmungen geprüft (analog Equi-Join),
Das vorliegende Beispiel ist genau so gewählt, dass in beiden Relationen eine Spalte A und eine Spalte B existiert. Genau wie beim Equi-Join werden A=A und B=B geprüft. Im Anschluss an die Zusammenführung werden die Spalten paarweise zu einer einzigen A- bzw. B-Spalte zusammengefasst.

X
A B
1 2
2 3
3 4
Y
A B C
1 2 3
2 3 4
3 4 5
Natural Join von X, Y
A B C
1 2 3
2 3 4
3 4 5

Einige Datenbanksysteme erkennen das Schlüsselwort NATURAL und eliminieren entsprechend automatisch doppelte Spalten.

Die in Datenbanken weiterhin gebräuchlichen Joins werden in nachfolgenden Abschnitten separat behandelt:

Syntax:

Datenbanksysteme unterstützen verschiedene Schreibweisen für Joins, die sich zum einen in ihrer Anwendbarkeit und zum anderen in der Lesbarkeit unterscheiden.

Beispiel Bedeutung
SELECT X.a, X.b, Y.a, Y.b, c
FROM X, Y
implizite Schreibweise
SELECT X.a, X.b, Y.a, Y.b, c
FROM X JOIN Y
explizite Schreibweise

Erklärung:

Zunächst fällt auf, dass die Spalten A und B jeweils mit dem Tabellennamen angegeben werden. Der Grund liegt darin, dass das Datenbanksystem bei der Angabe a nicht entscheiden kann, welche der Spalten gemeint ist, die aus Tabelle X oder die aus Tabelle Y. Um die Doppeldeutigkeit aufzulösen musst der Tabellenname angegebenen werden.
Desweiteren ist die implizite Schreibweise kürzer als die explizite. Sollen allerdings in einer Abfrage einmal mehrere Tabellen gejoint werden, kann es anschließend schwierig sein herauszufinden, welche Tabelle wann mit welcher und unter welchen Bedingungen gejoint wird.

Platzierung von Kriterien

In der Regel sollen für den Join Kriterien festgelegt werden (siehe z.B. Equi-Join weiter oben). Grundsätzlich besteht die Möglichkeit, diese Kriterien im Bereich der WHERE-Klausel einer Abfrage anzugeben.

SELECT c FROM X, Y WHERE X.a = Y.a AND X.b = Y.b

Im Fall der impliziten Schreibweise müssen die Kriterien hier platziert werden, bei der expliziten können sie so platziert werden. Der Leser möge sich aber den Unterschied klar machen: Wenn das DBMS die Kriterien nicht dem Join zuordnen kann, so wird zunächst ein vollständiges Kreuzprodukt gebildet und erst am Ende der Abfrage per WHERE auf die relevante Menge reduziert. Speziell bei bei einer Abfolge von mehreren Joins steigt dadurch die Datenmenge unnötig an.

Bei der expliziten Schreibweise ist daher auch die gezielte Angabe der Bedingungen anzustreben, zumal sie auch die Lesbarkeit der Abfrage erhöht.

SELECT c FROM X JOIN Y ON X.a = Y.a AND X.b = Y.b

Das Schlüsselwort ON leitet eine Kriterienblock ein, der dem WHERE-Block des gesamten SELECT-Statements ähnlich ist. Zu jedem Join gehört genau ein oder kein ON-Block.

In allen bisherigen Beispiele waren die Spaltennamen, die als Kriterien dienen, identisch. Außerdem wurde die Gleichheit der Werte in beiden Spalten gefordert. Einige Datenbanksysteme bieten für diesen "Spezialfall" auch eine gesonderte Schreibweise an:

SELECT c FROM X JOIN Y USING (a, b)

Anmerkung: Sämtliche Beispiele haben eine Gleichheit der Spalten A und B gefordert. In der Praxis ist dies wohl auch die am häufigsten benötigte Variante. Es spricht allerdings nichts dagegen, einen Join ON X.a < Y.a o.ä. auszuführen.

Mehrfachjoins:

Ein einzelner Join führt immer genau zwei Tabellen zusammen. Die Namen LEFT (OUTER) JOIN bzw. RIGHT (OUTER) JOIN sowie die nachfolgend verwendete Bezeichnung 'linke' bzw. 'rechte' Tabelle beziehen sich auf die Reihenfolge in der die Tabellen am Join beteiligt sind. Bei X JOIN Y ist 'X' die linke, 'Y' die rechte Tabelle.

Beispiel Bedeutung
SELECT a, b, c, d, e
FROM X, Y, Z
implizite Schreibweise
SELECT a, b, c, d, e
FROM X JOIN Y JOIN Z
explizite Schreibweise

Beide Schreibweisen können zu dem Eindruck führen, es würden mehr als zwei Tabellen gejoint. In Wahrheit ist dies nicht der Fall. Der Ausdruck wird i.d.R. von links nach rechts abgearbeitet, so dass zunächst X und Y gejoint werden. Das Ergebnis dieses Joins wird sodann mit Z gejoint. Dies sollte man im Hinterkopf haben, da die Reihenfolge der Tabellen und Platzierung von Kriterien sowohl die Performance als auch das Ergebnis beeinflussen können. Zum Thema 'Mehrfach-JOINs' sei der weiterführende Artikel Seite Fortgeschrittene Joins von Vinzenz Mai empfohlen.

nach obennach unten

INNER JOIN

Der INNER JOIN führt Datensätze aus der linken und rechten Tabelle genau dann zusammen, wenn die angegebenen Kriterien alle erfüllt sind. Ist eines oder mehrere der Kriterien nicht erfüllt, so entsteht kein Datensatz in der Ergebnismenge. Durch den Einsatz dieses JOINS reduziert sich das Ergebnis des kartesischen Produktes auf ein Minimum (vergleiche auch nachfolgende Join-Varianten).

Beispiel:

Gesucht werden alle Rechnungen vom Oktober, die mit Kreditkarte beglichen wurden.

Implizite Schreibweise des implizite Schreibweise des INNER JOIN

SELECT RechnungsNr, KundenNr, Betrag, Rechnungen_Oktober.kartennummer, Firma, Inhaber, Ablaufdatum
FROM Kreditkarte, Rechnungen_Oktober
WHERE Kreditkarte.Kartennummer = Rechnungen_Oktober.Kartennummer

Explizite Schreibweise des INNER JOIN

SELECT
    RechnungsNr,
    KundenNr,
    Betrag,
    Rechnungen_Oktober.Kartennummer,
    Firma, Inhaber,
    Ablaufdatum
FROM Kreditkarte
INNER JOIN Rechnungen_Oktober ON Kreditkarte.Kartennummer = Rechnungen_Oktober.Kartennummer

Beachten Sie, dass die Reihenfolge in der die Tabellen genannt werden bei diesem Join egal ist.

Das Ergebnis der Abfrage lautet:

Ergebnis
RechnungsNr KundenNr Betrag Kartennummer Firma Inhaber Ablaufdatum
98765 ABX039 49,95 12345 VISA Max Mustermann 05/2007
98767 ABX040 79,95 12347 American Express John Doe 01/2007
98768 ABX050 59,99 12347 American Express John Doe 01/2007
98769 ABX050 29,99 12348 Diners Club John Doe 03/2008

Erläuterung:

Zunächst fällt auf, dass aus ursprünglich 6 Rechnungen nun nur noch 4 übrig sind. Der Grund liegt darin, dass zu den beiden Barzahlungseinträgen (Kartennummer '-') keine passende Kreditkarte gefunden werden konnte. Damit ist das ON-Kriterium des Joins nicht erfüllt und der Datensatz entfällt. Analog ist die Kreditkarte '12346' nicht im Ergebnis enthalten, da sie keiner Rechnung zugeordnet werden konnte.

Im Vergleich zum kartesischen Produkt entfallen darüber hinaus all diejenigen Datensätze, bei denen zwar in beiden Tabellen eine Kreditkartennummer vorhanden ist, diese aber nicht übereinstimmt.

Beachten Sie: Die Spalte 'Kartennummer' muss zusammen mit einem Tabellennamen angegeben werden, da sie durch den Join im Ergebnis doppelt vorhanden ist, einmal aus jeder Tabelle (wenn auch mit identischem Wert). Das DBMS fordert daher zur Auflösung dieser Mehrdeutigkeit auf......

Das obige Ergebnis stimmt zudem mit einer nicht-normalisierten Speicherung aller Kreditkartenzahlungen überein, mit den bekannten Redundanzen (hier z.B. die Kreditkartendaten der Kartennummer '12347').

nach obennach unten

LEFT/RIGHT JOIN

Die LEFT JOIN- bzw. RIGHT JOIN-Varianten sind auf den ersten Blick etwas schwieriger zu verstehen. Die Syntax ist dabei bis auf das jeweilige Schlüsselwort analog der Syntax des INNER JOIN. Der Unterschied liegt in den Datensätzen, die hinterher im Ergebnis zugelassen werden.

Die Logik lautet für den LEFT JOIN: Ein Datensatz aus der linken Tabelle kommt in jedem Fall in das Ergebnis. Wenn ein Datensatz der rechten Tabelle dem ON-Kriterium entspricht, so wird er entsprechend in den Spalten eingetragen, ansonsten bleiben die Spalten leer (null). Der RIGHT JOIN arbeitet genau entgegengesetzt.

Beachten Sie: In diesem Fall spielt die Reihenfolge in der die Tabellen angesprochen werden sehr wohl eine Rolle, Kreditkarte LEFT JOIN Rechnungen_Oktober (alle Kreditkarten, ggf. Rechnungsdaten) liefert ein anderes Ergebnis als Rechnungen_Oktober LEFT JOIN Kreditkarte (alle Rechnungen, ggf. Kreditkarteninformation).

Beispiel LEFT JOIN:

Gesucht werden alle Rechnungen vom Oktober. Falls sie per Kreditkarte bezahlt wurden, so sollen die Kartendaten ebenfalls ausgegeben werden.

SELECT
    RechnungsNr,
    KundenNr,
    Betrag,
    Rechnungen_Oktober.Kartennummer,
    Firma,
    Inhaber,
    Ablaufdatum
FROM Rechnungen_Oktober
LEFT JOIN Kreditkarte ON Kreditkarte.Kartennummer = Rechnungen_Oktober.Kartennummer

Beachten Sie, dass im Unterschied zum INNER JOIN die Herkunft der selektierte Kartennummer eine Rolle spielt. Dies ist mehr eine syntaktische Feinheit als inhaltliche Notwendigkeit - Für alle Datensätze trifft genau eines der nachfolgenden Szenarien zu:

  1. Rechnungen_Oktober.Kartennummer = Kreditkarte.Kartennummer: Beide Kartennummern stimmen überein, damit ist es egal aus welcher Tabelle die Kartennummer entstammt.
  2. Rechnungen_Oktober.Kartennummer ist NULL (Barzahlung): Der Join bringt kein Ergebnis. Per Definition ergibt damit der Ausdruck Kreditkarte.Kartennummer 'NULL', ebenso wie Rechnungen_Oktober.Kartennummer.
  3. Rechnungen_Oktober.Kartennummer ist eine nicht-existente Karte: Dies ist der kritische Sonderfall. Bei korrekt definierten Konsistenzbedingungen dürfte er nicht auftreten. Tut er dies doch, so ist Kreditkarte.Kartennummer 'NULL', da keine Übereinstimmung gefunden wurde. Rechnungen_Oktober.Kartennummer enthält jedoch die ungültige Kartennummer. Je nachdem welche Spalte im SELECT aufgeführt wurde, unterscheidet sich also das Ergebnis.

Das Ergebnis der Abfrage lautet:

Ergebnis
RechnungsNr KundenNr Betrag Kartennummer Firma Inhaber Ablaufdatum
98765 ABX039 49,95 12345 VISA Max Mustermann 05/2007
98766 ABX039 12,95 - - - -
98767 ABX040 79,95 12347 American Express John Doe 01/2007
98768 ABX050 59,99 12347 American Express John Doe 01/2007
98769 ABX050 29,99 12348 Diners Club John Doe 03/2008
98770 ABX060 99,99 - - - -

Erläuterung:

Es befinden sich nun alle 6 Rechnungen der Ausgangsmenge im Ergebnis, alle diejenigen zu denen es eine Karteninformation gab wurden um diese Informationen ergänzt.

Die Spalte 'Kartennummer' muss zusammen mit dem Tabellennamen angegeben werden, da sie durch den Join im Ergebnis doppelt vorhanden ist, einmal aus jeder Tabelle (wenn auch mit identischem Wert). Das DBMS fordert daher zur Auflösung dieser Mehrdeutigkeit auf......

Dieses Ergebnis entspricht der nicht normalisierten Speicherung aller Rechnungsinformationen.

Beispiel RIGHT JOIN:

Gesucht werden alle Karteninformationen. Falls mit der entsprechenden Kreditkarte im Oktober etwas bestellt wurde, sollen die Rechnungsinformationen beigefügt werden.

SELECT
    RechnungsNr,
    KundenNr,
    Betrag,
    Kreditkarte.Kartennummer,
    Firma,
    Inhaber,
    Ablaufdatum
FROM rechnungen_oktober RIGHT JOIN Kreditkarte
ON Kreditkarte.Kartennummer = Rechnungen_Oktober.Kartennummer

Beachten Sie, dass im Unterschied zum INNER JOIN die Herkunft der selektierte Kartennummer eine Rolle spielt. Dies ist mehr eine syntaktische Feinheit als inhaltliche Notwendigkeit - Für alle Datensätze trifft genau eines der nachfolgenden Szenarien zu:

  1. Kreditkarte.Kartennummer = Rechnungen_Oktober.Kartennummer: Beide Kartennummern stimmen überein, damit ist es egal aus welcher Tabelle die Kartennummer entstammt.
  2. Kreditkarte.Kartennummer ist NULL: Der Join bringt kein Ergebnis. Per Definition ergibt damit der Ausdruck Rechnungen_Oktober.Kartennummer NULL, ebenso wie Kreditkarte.Kartennummer.
    Dieser Auflistungpunkt wurde nur aus Gründen der Vollständigkeit eingefügt. Das vorgeführte Beispiel arbeitet mit einer Fremdschlüsselbeziehung: Der Primärschlüssel 'Kartennummer' aus der Tabelle 'Kreditkarte' tritt als Fremdschlüssel in der Tabelle 'Rechnungen_Oktober' auf. Beim LEFT JOIN machen diese Ausführungen demnach Sinn (es kann eine Rechnung geben, bei der keine Kreditkarte eingetragen ist). Dieses inverse RIGHT JOIN-Beispiel geht jedoch von einer Kreditkarte mit der Kartennummer NULL aus , was auf Grund der Primärschlüsseleigenschaft der Kartennummer nicht passieren kann.
  3. Kreditkarte.Kartennummer ist eine nicht-verwendete Karte: Dies ist der kritische Sonderfall. Wird Rechnungen_Oktober.Kartennummer abgefragt, so ist das Ergebnis NULL, da keine Übereinstimmung gefunden wurde. Kreditkarte.Kartennummer enthält jedoch die Nummer der nicht verwendeten Karte. Je nachdem welche Spalte im SELECT aufgeführt wurde, unterscheidet sich also das Ergebnis.

Hinweis: Das MySQL-Handbuch rät von der Verwendung von RIGHT JOIN ab, da dieser Join nicht von allen DBMS unterstützt wird und durch simples Umsortieren durch einen LEFT JOIN ersetzt werden kann.

Das Ergebnis der Abfrage lautet:

Ergebnis
RechnungsNr KundenNr Betrag Kartennummer Firma Inhaber Ablaufdatum
98765 ABX039 49,95 12345 VISA Max Mustermann 05/2007
- - - 12346 Mastercard Katrin Musterfrau 01/2008
98767 ABX040 79,95 12347 American Express John Doe 01/2007
98768 ABX050 59,99 12347 American Express John Doe 01/2007
98769 ABX050 29,99 12348 Diners Club John Doe 03/2008

Erläuterung:

In der Ergebnismenge befinden sich alle 4 Kreditkarten, die Karte '12347' taucht doppelt auf, da sie zwei Rechnungen zugeordnet ist. Die Karte '12346' hingegen wird ohne Rechnung gelistet.

Die Spalte 'Kartennummer' muss zusammen mit dem Tabellennamen angegeben werden, da sie durch den Join im Ergebnis doppelt vorhanden ist, einmal aus jeder Tabelle (wenn auch mit identischem Wert). Das DBMS fordert daher zur Auflösung dieser Mehrdeutigkeit auf......

Dieses Ergebnis entspricht der nicht normalisierten Speicherung aller Kreditkarteninformationen.

nach obennach unten

FULL OUTER JOIN

Der FULL OUTER JOIN kommt dem ursprünglichen Kreuzprodukt von allen Joins am nächsten. Er ist gewissermaßen die Kombination aus LEFT und RIGHT-JOIN.

Die Logik für diesen Join: Jeder Datensatz der rechten und der linken Tabelle kommt in die Ergebnismenge. Findet sich über das ON-Kriterium ein passender Partner werden beide zusammengefügt, andernfalls wird die jeweils fehlende Seite mit NULL aufgefüllt.

Beispiel FULL OUTER JOIN:

Gesucht werden sowohl alle Karteninformationen als auch alle Rechnungen. Sofern möglich sollen dabei Rechnungen und Karten kombiniert werden.

SELECT
    RechnungsNr,
    KundenNr,
    Betrag,
    Rechnungen_Oktober.Kartennummer,
    Firma,
    Inhaber,
    Ablaufdatum
FROM Rechnungen_Oktober
OUTER JOIN Kreditkarte ON Kreditkarte.Kartennummer = Rechnungen_Oktober.Kartennummer

Beachten Sie, dass in diesem Fall die Reihenfolge der Tabellen wiederum egal ist. Achtung: MySQL unterstützt diese Abfrage nicht.

Das Ergebnis der Abfrage lautet:

Ergebnis
RechnungsNr KundenNr Betrag Kartennummer Firma Inhaber Ablaufdatum
98765 ABX039 49,95 12345 VISA Max Mustermann 05/2007
98766 ABX039 12,95 - - - -
- - - 12346 Mastercard Katrin Musterfrau 01/2008
98767 ABX040 79,95 12347 American Express John Doe 01/2007
98768 ABX050 59,99 12347 American Express John Doe 01/2007
98769 ABX050 29,99 12348 Diners Club John Doe 03/2008
98770 ABX060 99,99 - - - -

Erläuterung:

In der Ergebnismenge befinden sich nun alle 6 Bestellungen sowie alle 4 Kreditkarten, sofern möglich mit Datensätzen der jeweils anderen Tabelle zusammengeführt.

Die Spalte 'Kartennummer' muss zusammen mit dem Tabellennamen angegeben werden, da sie durch den Join im Ergebnis doppelt vorhanden ist, einmal aus jeder Tabelle (wenn auch mit identischem Wert). Das DBMS fordert daher zur Auflösung dieser Mehrdeutigkeit auf …

Das Ergebnis der Abfrage entspricht einer nicht normalisierten Speicherung aller Daten in einer Tabelle

nach obennach unten

Abschließende Anmerkungen

Im Zusammenhang mit Joins und normalisierten Tabellen sollte man einige Dinge im Hinterkopf behalten:

Beispiele zur Tabellenreihenfolge: Alle Rechnungen zur Kreditkarte '12347':

Beispiel 1

SELECT
    RechnungsNr,
    KundenNr,
    Betrag,
    Rechnungen_Oktober.Kartennummer,
    Firma,
    Inhaber,
    Ablaufdatum
FROM Rechnungen_Oktober
INNER JOIN Kreditkarte ON Kreditkarte.Kartennummer = Rechnungen_Oktober.Kartennummer
WHERE Kreditkarte.Kartennummer = 12347

Erläuterung: In der Ausgangsform ist diese Abfrage relativ ineffizient. Dank der Reihenfolge beim INNER JOIN werden alle Rechnungen mit allen Karteninformationen gegenübergestellt. Erst nach dem Join wird das WHERE-Kriterium angewandt. Unter Umständen optimiert das Datenbanksystem diese Abfrage automatisch, aber sie kann auch vom Programmierer selbst optimiert werden.

Beispiel 2

SELECT
    RechnungsNr,
    KundenNr,
    Betrag,
    Rechnungen_oktober.Kartennummer,
    Firma,
    Inhaber,
    Ablaufdatum
FROM Kreditkarte
INNER JOIN Rechnungen_Oktober ON  Kreditkarte.Kartennummer = Rechnungen_Oktober.Kartennummer
                              AND Kreditkarte.Kartennummer = 12347

Bei dieser Abfrage wurden die verwendeten Tabellen vertauscht und im ON-Kriterium eine Einschränkung platziert. Die WHERE-Klausel wurde somit weiter nach innen verlegt.

Vorteil dieser Schreibweise: Sie wird von den meisten DBMS unterstützt und verwendet keinen Subselect.

Beispiel 3

SELECT
    RechnungsNr,
    KundenNr,
    Betrag,
    Rechnungen_oktober.Kartennummer,
    Firma,
    Inhaber,
    Ablaufdatum
FROM
    (SELECT Kartennummer, Firma, Inhaber, Ablaufdatum
    FROM Kreditkarte
    WHERE Kartennummer = 12347) Karte
INNER JOIN Rechnungen_Oktober
ON Karte.Kartennummer = Rechnungen_Oktober.Kartennummer

Dies ist die Subselect-Variante, die nicht von allen DBMS unterstützt wird (vgl. z.B. ältere Versionen von MySQL oder MS-Access).

Der Vorteil dieser Schreibweise gegenüber der vorherigen liegt in der Parametrisierbarkeit: Man stelle sich vor, man möchte eine Abfrage innerhalb einer Anwendung mehrfach verwenden, der Kriterienausdruck soll allerding dynamisch bleiben. In PHP-Syntax könnte sodann der gesamte Teil SELECT ... FROM ... WHERE stehen bleiben, einzig und alleine hinter dem WHERE wird eine Variable $(kriterienausdruck) platziert, die beliebig komplex sein kann. Da es sich um ein gewöhnliches SELECT FROM WHERE handelt, können hier auch Gruppierungen o.ä. vorgenommen werden, die in der ON-Klausel unzulässig sind.

Views

Es muss nicht immer ein Join sein. Wenn immer wieder die selbe Abfolge von Joins benötigt wird kann Gebrauch von sog. 'Views' gemacht werden. Ein View ist eine Art virtuelle Tabelle. Er basiert auf einer beliebig komplexen SQL-Abfrage und stellt das Ergebnis in Form einer Tabelle zur Verfügung. Das Datenbanksystem kann das Ergebnis dieser Abfrage unter Umständen cachen und somit beim zweiten Zugriff schneller zur Verfügung stellen als das Ergebnis des Joins. Beim Lesezugriff auf einen View gibt es i.d.R. keine Probleme, Schreiboperationen hingegen sind mit Vorsicht zu genießen.

Teil von SELFHTML aktuell Teil von Artikel Teil von Datenbanken

© 2007 bereichsübergreifende Seite Impressum, für diese Seite: E-Mail thimmsd@gmx.de