Zum Inhalt springen

Join (SQL)

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 8. Februar 2019 um 11:39 Uhr durch Wikiolo (Diskussion | Beiträge) (Referenz-Tabelle: BKL-Link entfernt). Sie kann sich erheblich von der aktuellen Version unterscheiden.

Ein SQL-Join verknüpft eine oder mehrere Spalten einer relationalen Datenbank, um das Ergebnis als Tabelle auszugeben oder weiterzuverarbeiten. Ein JOIN ermöglicht das Verknüpfen der Spalten einer einzelnen Tabelle (self-Join) oder mehrerer Tabellen, die gemeinsame Werte besitzen. Der ANSI-Standard für SQL beschreibt fünf Arten von JOINs: INNER, LEFT OUTER, RIGHT OUTER, FULL OUTER und CROSS. Als Spezialfall kann eine Tabelle (Basistabelle, View oder gejointe Tabelle) ein JOIN auf sich selbst durchführen (SELF JOIN).

Ein Programmierer deklariert eine JOIN-Anweisung, um mögliche Zeilen für einen Join zu identifizieren. Ein wahres Prädikat resultiert in der Ausgabe in einem entsprechenden Format, einem Datensatz oder einer temporären Tabelle.

Referenz-Tabelle

Relationale Datenbanken sind für gewöhnlich normalisiert um Duplikate, wie sie bei Entitätstypen mit "one-to-many"-Verknüpfungen bestehen, zu eliminieren. So kann zum Beispiel eine Abteilung mehrere Angestellte haben. Ein Join der Tabellen Abteilung und Angestellte ist eine effektive Möglichkeit, um eine neue Tabelle zu erstellen, welche alle Informationen beider Tabellen enthält.

Alle nachfolgenden Erklärungen über die verschiedenen Arten, Wirkweisen und die Prädikate von Joins verwenden die beiden weiter unten dargestellten Tabellen.

In den folgenden Tabellen ist die Spalte AbteilungID der Tabelle Abteilung (auch Abteilung.AbteilungID genannt) der Primärschlüssel, während hingegen Angestellter.AbteilungID den Fremdschlüssel auszeichnet.

Tabelle: Angestellter
Nachname AbteilungID
Müller 31
Schmidt 33
Schneider 33
Fischer 34
Weber 34
Meyer NULL
Tabelle: Abteilung
AbteilungID AbteilungName
31 Verkauf
33 Technik
34 Büro
35 Marketing

Anmerkung: In der obigen Tabelle Angestellter wurde dem Angestellten "Meyer" noch keine Abteilung zugewiesen. Außerdem gehört kein Angestellter der Abteilung "Marketing" an.

Die folgende SQL-Anweisung erzeugt die zuvor genannten Tabellen:

CREATE TABLE Abteilung
(
 AbteilungID INT Primary key,
 AbteilungName VARCHAR(20)
);

CREATE TABLE Angestellter
(
 Nachname VARCHAR(20),
 AbteilungID INT references Abteilung(AbteilungID)
);

INSERT INTO Abteilung VALUES(31, 'Verkauf');
INSERT INTO Abteilung VALUES(33, 'Technik');
INSERT INTO Abteilung VALUES(34, 'Büro');
INSERT INTO Abteilung VALUES(35, 'Marketing');

INSERT INTO Angestellter VALUES('Müller', 31);
INSERT INTO Angestellter VALUES('Schmidt', 33);
INSERT INTO Angestellter VALUES('Schneider', 33);
INSERT INTO Angestellter VALUES('Fischer', 34);
INSERT INTO Angestellter VALUES('Weber', 34);
INSERT INTO Angestellter VALUES('Meyer', NULL);

CROSS JOIN

Ein CROSS JOIN gibt das kartesische Produkt der Tabellenzeilen eines Joins wieder. Mit anderen Worten: Ein neuer Datensatz entsteht durch die Verknüpfung jeder Zeile der einen Tabelle mit jeder Zeile der anderen Tabelle.[1]

Beispiel eines expliziten CROSS JOINs:

SELECT *
FROM Angestellter CROSS JOIN Abteilung;

Beispiel eines impliziten CROSS JOINs:

SELECT *
FROM Angestellter, Abteilung;
Angestellter.Nachname Angestellter.AbteilungID Abteilung.AbteilungName Abteilung.AbteilungID
Müller 31 Verkauf 31
Schmidt 33 Verkauf 31
Schneider 33 Verkauf 31
Weber 34 Verkauf 31
Fischer 34 Verkauf 31
Meyer NULL Verkauf 31
Müller 31 Technik 33
Schmidt 33 Technik 33
Schneider 33 Technik 33
Weber 34 Technik 33
Fischer 34 Technik 33
Meyer NULL Technik 33
Müller 31 Büro 34
Schmidt 33 Büro 34
Schneider 33 Büro 34
Weber 34 Büro 34
Fischer 34 Büro 34
Meyer NULL Büro 34
Müller 31 Marketing 35
Schmidt 33 Marketing 35
Schneider 33 Marketing 35
Weber 34 Marketing 35
Fischer 34 Marketing 35
Meyer NULL Marketing 35

Ein CROSS JOIN verwendet kein Prädikat, um die Zeilen der gejointen Tabelle zu filtern. Eine zusätzliche WHERE-Bedingung kann jedoch den CROSS JOIN filtern, um ihn in einen INNER JOIN umzuwandeln.

Der SQL:2011-Standard listet CROSS JOINs als Teil des optionalen F401-Pakets Extended joined table auf.

Ein typischer Einsatz sind Checks der Leistungsfähigkeit von Servern.

INNER JOIN

A Venn Diagram: Innerer Überlapp ausgefüllt.
Venn Diagram: INNER JOIN zweier Tabellen A und B

Für einen INNER JOIN müssen in jeder Zeile der zwei zu joinenden Tabellen übereinstimmende Spalten-Werte vorhanden sein. Software enthält oft diese Join-Operation, sie gilt aber nicht als beste Lösung für alle Probleme. Ein INNER JOIN erstellt eine neue Ergebnis-Tabelle, indem er die Spalten-Werte der zwei Tabellen A und B aufgrund ihres Join-Prädikats miteinander verbindet. Die Abfrage vergleicht jede Zeile von Tabelle A mit jeder Zeile von Tabelle B, um alle Paare zu finden, die dem Join-Prädikat genügen. Besitzen diese Paare keine NULL-Werte, generieren sie eine vereinigte Ergebnis-Zeile.

Das Ergebnis eines Joins entspricht einem kartesischen Produkt (oder CROSS JOIN) aller Zeilen der Tabellen A und B (jede Zeile der Tabelle A mit jeder Zeile der Tabelle B), bei welchem bei anschließender Ausgabe alle Zeilen dem Join-Prädikat genügen.

Gegenwärtige SQL-Umsetzungen verwenden für gewöhnlich Ansätze wie Hash-Join oder Sort-Merge-Join, da das Berechnen eines kartesischen Produkts viel langsamer wäre und zu viel Arbeitsspeicher benötigte.

In SQL sind zwei syntaktische Arten von Joins definiert: die "explizite" Join-Notation und die "implizite" Join-Notation. Die implizite Join-Notation gilt nicht mehr als Best practice, Datenbanksysteme unterstützen sie jedoch weiterhin. Die explizite Join-Notation verwendet das JOIN-Schlüsselwort, optional mit dem INNER-Schlüsselwort vorangestellt, um die Tabelle zu benennen, und das ON-Schlüsselwort, um das Join-Prädikat zu definieren.

Beispiel für die explizite Join-Notation:

SELECT Angestellter.Nachname, Angestellter.AbteilungID, Abteilung.AbteilungName
FROM Angestellter
INNER JOIN Abteilung ON
Angestellter.AbteilungID = Abteilung.AbteilungID;

Ergebnis:

Angestellter.Nachname Angestellter.AbteilungID Abteilung.AbteilungName
Fischer 34 Büro
Schmidt 33 Technik
Weber 34 Büro
Schneider 33 Technik
Müller 31 Verkauf

In der impliziten Join-Notation sind die zu joinenden Tabellen in der FROM-Bedingung der SELECT-Abfrage lediglich durch ein Komma getrennt. Mit einer zusätzlichen WHERE-Bedingung auf den dadurch durchgeführten CROSS JOIN sind weitere Filter-Prädikate möglich, die analog zur expliziten Join-Notation funktionieren. Diese implizite Join-Notation führt zum gleichen Ergebnis wie die explizite Notation:

SELECT *
FROM Angestellter, Abteilung
WHERE Angestellter.AbteilungID = Abteilung.AbteilungID;

Die Abfragen der obigen Beispiele joinen die Tabellen Angestellter und Abteilung, indem sie die gemeinsame Spalte AbteilungID verwenden. Passt die AbteilungID der Tabellen (das Join-Prädikat ist erfüllt), verbindet die Abfrage die Spalten Nachname, AbteilungID und AbteilungName der beiden Tabellen zu einer Ergebnis-Zeile. Bei nicht übereinstimmender AbteilungID, generiert sich keine neue Ergebniszeile. Das Ergebnis des Auswertungsplans der Abfrage sieht folgendermaßen aus:

Angestellter.Nachname Angestellter.AbteilungID Abteilung.AbteilungName Abteilung.AbteilungID
Fischer 34 Büro 34
Schmidt 33 Technik 33
Weber 34 Büro 34
Schneider 33 Technik 33
Müller 31 Verkauf 31

Der Angestellte "Meyer" und die Abteilung "Marketing" tauchen nach der Ausführung der Abfrage nicht als Ergebnisse auf. Keiner von beiden weist eine passende Zeile in der entsprechend anderen Tabelle auf: "Meyer" hat keine entsprechende Abteilung, und kein Angestellter hat die AbteilungId 35 ("Marketing"). Abhängig von den gewünschten Resultaten, ist dieses Verhalten ein simpler Bug, den das Ersetzen des INNER JOIN durch einen OUTER JOIN, fixt.

Bei einem Join zweier Tabellen, die NULL-Werte enthalten, ist besondere Vorsicht angebracht. Bei nicht übereinstimmender AbteilungID, generiert sich keine neue Ergebniszeile. In diesem Fall stimmt kein NULL-Wert mit einem anderen Wert überein (auch nicht mit dem NULL-Wert selbst). Die explizite Verwendung eines modifizierten Prädikats in der Join-Bedingung berücksichtigt diese Einträge, wobei bei Anwendung der restlichen Prädikatsbedingungen die Join-Spalten den Wert NOT NULL aufweisen müssen.

Der INNER JOIN ist nur bei Datenbanken zuverlässig, die referentielle Integrität erzwingen oder in Fällen, in denen die Join-Spalten mit Sicherheit keine NULL-Werte enthalten. Viele transaktionsverarbeitende relationale Datenbanken basieren auf dem ACID-Prinzip-Standard für Datenupdates, um die Datenintegrität zu gewährleisten, weshalb INNER JOINs eine gute Wahl sind. Transaktionsdatenbanken haben jedoch meist erwünschte Join-Spalten, die NULL-Werte enthalten können. Manche reportende relationalen Datenbanken und Data-Warehouses verwenden Batch-Updates für ETL-Prozesse, die den Zwang zur Erhaltung der referentielle Integrität schwierig oder unmöglich machen, wodurch Join-Spalten mit NULL-Werten entstehen können, welche der Autor der SQL-Abfrage nicht verändern kann und welche bei einem INNER JOINs zu einem Verlust von Datensätzen führen kann, ohne dass es Anzeichen für Fehler gibt. Die Entscheidung für oder gegen einen INNER JOIN hängt vom Datenbankdesign und den Besonderheiten der Datensätze ab. Ein INNER JOIN kann für gewöhnlich einen LEFT OUTER JOIN ersetzen, falls die Join-Spalten in einer Tabelle NULL-Werte enthalten.

Spalten mit NULL-Werten eignen sich nur für INNER JOINs, falls diese entsprechende Zeilen mit NULL-Werten entfernen sollen. In diesem Fall kann ein INNER JOIN sogar schneller als ein OUTER JOIN sein, da der Join und das Filtern ein einziger Schritt sind. Andererseits kann ein INNER JOIN bei einer hochvolumigen Abfrage in Kombination mit Datenbankfunktionen in einer WHERE-Bedingung zu verheerend langsamer Performance oder sogar zu einem Serverabsturz führen.[2][3][4]

Eine Funktion in einer WHERE-Bedingung kann dazu führen, dass eine Datenbank relativ kompakte Tabellenindizes ignoriert. Unter Umständen liest die Datenbank die ausgewählten Spalten der beiden Tabellen aus und führt einen INNER JOIN mit ihnen aus, bevor ein Filter mit Abhängigkeit von einem vorher berechneten Wert die Anzahl der Zeilen reduziert. Dies führt schließlich zu einer ineffizienten Prozessverarbeitung. Sollte ein Join ein Ergebnis-Set generieren, das Master-Tabellen enthält, die zum Nachschlagen ganzer Text-Beschreibungen numerischer Kennzahlen dienen (Lookup-Tabelle), kann ein NULL-Wert in einem der Fremdschlüssel eine Löschung der gesamten Zeile aus dem Ergebnis-Set ohne Hinweis auf einen Fehler verursachen. Eine komplexe SQL-Abfrage, die eine oder mehrere INNER JOINs und mehrere OUTER JOINs enthält, birgt das gleiche Risiko für NULL-Werte in den verknüpften Spalten des INNER JOINs. Ein Commit für SQL-Code, das NULL-Werte von INNER JOINs berücksichtigt, ist in zukünftigen Änderungen also Updates von Anbietern, Veränderungen am Design, Stapelverarbeitung außerhalb der Regeln der Datenvalidierung der Anwendung (wie Datenkonvertierung), Migrationen, Bulk-Importe und Merges, nicht vorgesehen.

Der folgende Abschnitt beschreibt die Unterteilung des INNER JOINs in EQUI JOIN, als NATURAL JOIN und CROSS JOIN.

EQUI JOIN

Ein EQUI JOIN ist ein vergleichsbasierter Join, welcher nur den Gleichheits-Vergleich im Join-Prädikat verwendet. Andere benutzte Vergleichsoperatoren (wie z. B.: <) schließen einen EQUI JOIN aus. Die obige Abfrage enthält bereits ein Beispiel für einen EQUI JOIN:

SELECT *
FROM Angestellter JOIN Abteilung
  ON Angestellter.AbteilungID = Abteilung.AbteilungID;

oder mit folgender Notation:

SELECT *
FROM Angestellter, Abteilung
WHERE Angestellter.AbteilungID = Abteilung.AbteilungID;

Der SQL-92-Standard liefert eine optionale Kurznotation, um Spalten gleichen Namens mit dem USING-Konstrukt zu beschreiben:[5]

SELECT *
FROM Angestellter INNER JOIN Abteilung USING (AbteilungID);

Das USING-Konstrukt ist mehr als nur Syntaktischer Zucker, da das Resultat sich von der Abfrage mit explizitem Prädikat unterscheidet. Alle ausgegebenen Spalten der USING-Liste haben einen unbestimmten Namen, statt je einem für jede Tabelle des Joins. Das obige Beispiel ergibt eine einzelne Spalte AbteilungID und keine Angestellter.AbteilungID- oder Abteilung.AbteilungID-Spalte.

MS SQL Server und Sybase unterstützen die USING-Bedingung nicht.

NATURAL JOIN

Der NATURAL JOIN ist ein Spezialfall des EQUI JOINs. Der NATURAL JOIN (⋈) ist eine Zweistellige Relation mit Notation RS (R und S als Relationen). Das Kennzeichen für die Fliege ⋈ in Unicode ist U+22C8. Das Ergebnis eines NATURAL JOINs ist das Set aller Tupel-Kombinationen von R und S, welche identische Namensattribute besitzen. Das folgende Beispiel verdeutlicht den NATURAL JOIN anhand der Tabellen Angestellter und Abteilung:

Angestellter
Name AngID AbtName
Ben 3415 Finanzen
Mia 2241 Verkauf
Paul 3401 Finanzen
Emma 2202 Verkauf
Abteilung
AbtName Manager
Finanzen Paul
Verkauf Emma
Production Charles
Angestellter  Abteilung
Name AngID AbtName Manager
Ben 3415 Finanzen Paul
Mia 2241 Verkauf Emma
Paul 3401 Finanzen Paul
Emma 2202 Verkauf Emma

Diese Definition lässt sich auch für eine Relation verwenden. Zum Beispiel ist die Verkettung von Angestellter und Abteilung der obige Join, projiziert auf alles außer das gemeinsamen Attribut AbtName. In der Kategorientheorie entspricht dies dem Faserprodukt.

Der NATURAL JOIN ist wohl einer der wichtigsten Operatoren, da er die relationale Entsprechung des logischen AND darstellt. Erscheint die gleiche Variable in zwei Prädikaten, die mit einem AND verbunden sind, so steht diese Variable immer bei beiden für den gleichen Wert. Der NATURAL JOIN erlaubt vor allem die Kombination von mit Fremdschlüssel assoziierten Relationen. Zum Beispiel verbindet vermutlich ein Fremdschlüssel Angestellter.AbtName mit Abteilung.AbtName und der NATURAL JOIN von Angestellter mit Abt verbindet alle Angestellten mit ihren Abteilungen. Das funktioniert, weil der Fremdschlüssel Attribute mit dem gleichen Namen enthält. Beim gegenteiligen Beispiel des Fremdschlüssels von Abt zu Angestellter müssen vor der Verwendung des NATURAL JOINs diese Spalten noch einen neuen Namen erhalten. Diese Art von Join heißt EQUI JOIN.

Die formalere Semantik des NATURAL JOINs ist wie folgt definiert:

,

dabei ist ein Prädikat, welches wahr ist, und zwar dann und nur dann, wenn r eine Funktion ist. Für gewöhnlich müssen dafür R und S mindestens ein gemeinsames Attribut besitzen, ansonsten entspricht der NATURAL JOIN dem kartesischen Produkt.

Die Codd-Funktionen können den NATURAL JOIN konstruieren:

Seien c1, …, cm die gemeinsamen Attributsnamen von R und S, r1, …, rn seien die einzigarten Attributsnamen für R und seien s1, …, sk die einzigartigen Attributsnamen für S. Außerdem seien die Attributsnamen x1, …, xm weder in R noch in S enthalten. Im ersten Schritt erhalten die Attribute in S einen neuen Namen:

Anschließend wenden wir das kartesische Produkt an und wählen die zu joinenden Tupel aus:

Ein NATURAL JOIN ist eine besondere Form des EQUI JOINs bei dem das Join-Prädikat implizit durch einen Vergleich aller Tabellen entsteht, die den jeweils gleichen Spalten-Namen in den zu joinenden Tabellen aufweisen. Die entstehende gejointe Tabelle besteht aus lediglich einer Spalte für jedes Paar gleichnamiger Spalten. Existieren keine Spalten mit gleichem Namen, ist das Ergebnis ein CROSS JOIN.

Die meisten Experten halten NATURAL JOINs für gefährlich und raten stark von ihrer Verwendung ab.[6] Die Gefahr besteht darin, versehentlich eine neue Spalte hinzuzufügen, welche den gleichen Namen trägt wie eine Spalte in einer anderen Tabelle. Ein NATURAL JOIN könnte diese neue Spalte dann für Vergleiche verwenden, die zu Vergleichen/Übereinstimmungen führen, welche unterschiedliche Bedingungen (für andere Spalten) als vorher benutzen. Eine solche Abfrage könnte daher verschiedene Ergebnisse erzeugen, auch wenn die Informationen in den Tabellen sich nicht geändert, sondern nur erweitert haben. Die Verwendung von Spalten-Namen, um automatisch Tabellen-Verknüpfungen zu bestimmen, ist bei großen Datenbanken mit hunderten oder tausenden von Tabellen keine Option, da dies eine unrealistische Beschränkung auf die Namenskonvention zur Folge hätte. Tatsächlich existierende Datenbanken sind für gewöhnlich mit Fremdschlüssel-Daten erstellt, welche aufgrund von Geschäftsregeln und Kontext nicht konsequent beschrieben sind (NULL-Werte sind erlaubt).

Dabei ist es übliche Praxis, Spalten-Namen ähnlicher Datensätze in verschiedenen Tabellen umzubenennen, und der Verlust starrer Konsistenz verbannt den NATURAL JOIN zurück in die theoretische Diskussion.

Eine andere Notation für die obige Beispiel-Abfrage eines INNER JOINs mit Hilfe eines NATURAL JOINs lautet:

SELECT *
FROM Angestellter NATURAL JOIN Abteilung;

Durch die explizite USING-Bedingung taucht nur eine Spalte AbteilungID ohne weiteres Attribut in der gejointen Tabelle auf:

AbteilungID Angestellter.Nachname Abteilung.AbteilungName
34 Weber Büro
33 Schmidt Technik
34 Fischer Büro
33 Schneider Technik
31 Müller Verkauf

PostgreSQL, MySQL und Oracle unterstützen NATURAL JOINs, Microsoft T-SQL und IBM DB2 jedoch nicht. Die im Join verwendeten Spalten sind implizit, weswegen der Join-Code die erwarteten Spalten nicht anzeigt und eine Veränderung der Spalten-Namen die Ergebnisse verändern kann. Im SQL:2011-Standard sind NATURAL JOINs Teil des optionalen F401-Pakets Extended joined table (zu Deutsch: erweiterte Join-Tabelle). In vielen Datenbankumgebungen sind die Spalten-Namen durch den Verkäufer bestimmt und nicht durch den Abfrageentwickler. Ein NATURAL JOIN nimmt Stabilität und Konsistenz in Spalten-Namen an, was sich durch von Verkäufern festgelegte Versionsupgrades ändern kann.

OUTER JOIN

Die gejointe Tabelle enthält jede Zeile, selbst wenn diese keine Paare bildet. Je nachdem welche Zeilen der Tabellen erhalten bleiben, ist ein OUTER JOINs ein LEFT OUTER-Join, RIGHT OUTER-Joins oder ein FULL OUTER-Join. Die Ausdrücke LEFT, RIGHT and FULL stehen für die rechte, linke oder beide Seite(n) des JOIN-Schlüsselworts.

Im SQL-Standard ist keine implizite Join-Notation für OUTER JOINs vorgesehen.

LEFT OUTER JOIN

A Venn Diagram: linker Kreis und Überlapp eingefärbt.
Venn-Diagramm: LEFT JOIN zweier Tabellen A und B.

Das Ergebnis eines LEFT OUTER JOINs (oder vereinfacht LEFT JOIN) der Tabellen A und B enthält alle Zeilen der linken ("LEFT") Tabelle A, selbst wenn die Join-Bedingung auf keine Spalte der rechten ("RIGHT") Tabelle B zutrifft.

Falls die ON-Bedingung für eine vorgegebene Zeile der Spalte A keine Zeile für die Tabelle B liefert, generiert der Join trotzdem die Zeile der Spalte A. Die entsprechenden Einträge für die Spalten der Tabelle B sind jedoch mit dem NULL-Wert ausgegeben. Ein LEFT OUTER JOIN gibt alle Werte eines INNER JOINs wieder zusätzlich aller Werte einer LEFT-Tabelle, welche nicht der RIGHT-Tabelle entsprechen und alle Zeilen mit NULL-Werten (also leeren Werten) der linken Spalte.

Wir können nun die Abteilung eines jeden Angestellten finden und trotzdem diejenigen Angestellten auflisten, welche noch keiner Abteilung zugewiesen worden sind (im Gegensatz zum INNER JOIN-Beispiel vorher, bei dem diese Angestellten ausgeschlossen wurden.)

Beispiel eines LEFT OUTER JOINs (das OUTER-Schlüsselwort ist optional) mit der zusätzlichen kursiv geschriebenen Zeile (verglichen mit dem INNER JOIN):

SELECT *
FROM Angestellter
LEFT OUTER JOIN Abteilung ON Angestellter.AbteilungID = Abteilung.AbteilungID;
Angestellter.Nachname Angestellter.AbteilungID Abteilung.AbteilungName Abteilung.AbteilungID
Schmidt 33 Technik 33
Müller 31 Verkauf 31
Fischer 34 Büro 34
Weber 34 Büro 34
Meyer NULL NULL NULL
Schneider 33 Technik 33

Alternative Syntax: Oracle unterstützt die veraltete Syntax.[7]

SELECT *
FROM Angestellter, Abteilung
WHERE Angestellter.AbteilungID = Abteilung.AbteilungID(+);

Sybase unterstützt die folgende Syntax (Microsoft SQL Server unterstützt diese Version seit 2000 nicht mehr):

SELECT *
FROM Angestellter, Abteilung
WHERE Angestellter.AbteilungID *= Abteilung.AbteilungID;

IBM Informix unterstützt diese Syntax:

SELECT *
FROM Angestellter, OUTER Abteilung
WHERE Angestellter.AbteilungID = Abteilung.AbteilungID;

RIGHT OUTER JOIN

Venn Diagram: rechter Kreis und Überlapp eingefärbt.
Venn-Diagramm: RIGHT JOIN zweier Tabellen A and B.

Ein RIGHT OUTER JOIN (auch RIGHT JOIN) ähnelt stark einem LEFT OUTER JOIN mit umgekehrter Abarbeitung der Tabellenreihenfolge. Jede Zeile der rechten ("RIGHT") Tabelle B kommt in der gejointen Tabelle mindestens einmal vor. Im Falle keiner passenden Zeile der linken ("LEFT") Tabelle A erscheint der NULL-Wert in den Spalten von A für die Zeilen, welche keine Entsprechung in B haben. Ein RIGHT OUTER JOIN gibt alle Werte der rechten ("RIGHT") Tabelle und passenden Werte der linken ("LEFT") Tabelle wieder (im Falle keiner passendenen Join-Prädikate NULL). Daher können wir zum Beispiel jeden Angestellten und seine Abteilung finden, aber dennoch Abteilungen anzeigen lassen, die keine Angestellten besitzen. Weiter unten ist ein Beispiel eines RIGHT OUTER JOINs (das OUTER-Schlüsselwort ist optional) mit der Ergebniszeile in kursiver Schrift:

SELECT *
FROM Angestellter RIGHT OUTER JOIN Abteilung
  ON Angestellter.AbteilungID = Abteilung.AbteilungID;
Angestellter.Nachname Angestellter.AbteilungID Abteilung.AbteilungName Abteilung.AbteilungID
Weber 34 Büro 34
Schmidt 33 Technik 33
Fischer 34 Büro 34
Schneider 33 Technik 33
Müller 31 Verkauf 31
NULL NULL Marketing 35

RIGHT und LEFT OUTER JOIN sind funktional äquivalent. Keiner von beidem besitzt eine Funktionalität, welche der andere nicht hat. Sie können sich also durch eine Vertauschung der Tabellenreihenfolge gegenseitig ersetzen.

FULL OUTER JOIN

Venn Diagram: rechter Kreis, linker Kreis und Überlapp eingefärbt.
Venn-Diagramm: FULL JOIN zweier Tabellen A and B.

Ein FULL OUTER JOIN verbindet die Resultate eines LEFT und eines RIGHT OUTER JOINs. Nicht passende Zeileneinträge eines FULL OUTER JOINs erhalten NULL-Werte für jede Spalte der Tabelle, denen eine passende Zeile fehlt. Passende Zeilen erzeugen eine einzelne Zeile im Ergebnis-Set, welche die Spalten beider Tabellen erhält. Zum Beispiel können wir dadurch jeden Angestellten anzeigen, der einer Abteilung zugewiesen ist, und jede Abteilung, die einen Angestellten hat, aber zusätzlich auch Angestellte, die nicht Teil einer Abteilung sind, oder Abteilungen, die keine Angestellten haben.

Beispiel für einen FULL OUTER JOIN (das OUTER-Schlüsselwort ist optional):

SELECT *
FROM Angestellter FULL OUTER JOIN Abteilung
  ON Angestellter.AbteilungID = Abteilung.AbteilungID;
Angestellter.Nachname Angestellter.AbteilungID Abteilung.AbteilungName Abteilung.AbteilungID
Weber 34 Büro 34
Schmidt 33 Technik 33
Fischer 34 Büro 34
Meyer NULL NULL NULL
Schneider 33 Technik 33
Müller 31 Verkauf 31
NULL NULL Marketing 35

Manche Datenbanksysteme unterstützen die Funktionalität des FULL OUTER JOINs nicht direkt. Sie können diesen aber durch den Gebrauch eines INNER JOINs und eines UNION ALL imitieren, indem sie die alleinstehenden Zeilen der rechten ("RIGHT") und der linken ("LEFT") Tabelle sowie den INNER JOIN mit einem UNION ALL verbinden.

Das gleiche Beispiel sieht dann wie folgt aus:

SELECT Angestellter.Nachname, Angestellter.AbteilungID,
       Abteilung.AbteilungName, Abteilung.AbteilungID
FROM Angestellter
INNER JOIN Abteilung ON Angestellter.AbteilungID = Abteilung.AbteilungID

UNION ALL

SELECT Angestellter.Nachname, Angestellter.AbteilungID,
       cast(NULL as varchar(20)), cast(NULL as integer)
FROM Angestellter
WHERE NOT EXISTS (
    SELECT * FROM Abteilung
             WHERE Angestellter.AbteilungID = Abteilung.AbteilungID)

UNION ALL

SELECT cast(NULL as varchar(20)), cast(NULL as integer),
       Abteilung.AbteilungName, Abteilung.AbteilungID
FROM Abteilung
WHERE NOT EXISTS (
    SELECT * FROM Angestellter
             WHERE Angestellter.AbteilungID = Abteilung.AbteilungID)

Alternativen

Ein UNION ALL zwischen einem INNER JOIN und einem SELECT der Zeilen der Haupttabelle, welche nicht die Join-Bedingung erfüllen, erzielt das gleiche Ergebnis wie ein OUTER JOIN.

Zum Beispiel ist

SELECT Angestellter.Nachname, Angestellter.AbteilungID, Abteilung.AbteilungName
FROM Angestellter
LEFT OUTER JOIN Abteilung ON Angestellter.AbteilungID = Abteilung.AbteilungID;

eine äquivalente Notation zu:

SELECT Angestellter.Nachname, Angestellter.AbteilungID, Abteilung.AbteilungName
FROM Angestellter
INNER JOIN Abteilung ON Angestellter.AbteilungID = Abteilung.AbteilungID

UNION ALL

SELECT Angestellter.Nachname, Angestellter.AbteilungID, cast(NULL as varchar(20))
FROM Angestellter
WHERE NOT EXISTS (
    SELECT * FROM Abteilung
             WHERE Angestellter.AbteilungID = Abteilung.AbteilungID)

SELF JOIN

Ein SELF JOIN ist ein Join einer Tabelle auf sich selbst.[8]

Im Falle zweier separater Tabellen für die Angestellten und einer Abfrage nach dem gleichen Land beider Tabellen könnte zum Beispiel eine gewöhnliche Join-Operation die Antwort-Tabelle ausgeben. Sehr oft finden sich jedoch alle Informationen innerhalb einer einzigen großen Tabelle.[9]

Die folgende Angestellter-Tabelle sei gegeben:

Tabelle: Angestellter
AngestellterID Nachname Land AbteilungID
123 Müller Australien 31
124 Schmidt Australien 33
145 Schneider Australien 33
201 Fischer Amerika 34
305 Weber Deutschland 34
306 Meyer Deutschland NULL

Eine mögliche Abfrage wäre:

SELECT F.AngestellterID, F.Nachname, S.AngestellterID, S.Nachname, F.Land
FROM Angestellter F INNER JOIN Angestellter S ON F.Land = S.Land
WHERE F.AngestellterID < S.AngestellterID
ORDER BY F.AngestellterID, S.AngestellterID;

Dies würde folgende Tabelle als Resultat ergeben:

Tabelle: Angestellter nach SELF JOIN mit Land
AngestellterID Nachname AngestellterID Nachname Land
123 Müller 124 Schmidt Australien
123 Müller 145 Schneider Australien
124 Schmidt 145 Schneider Australien
305 Weber 306 Meyer Deutschland

Zu diesem Beispiel:

  • F und S sind Aliase für die erste und zweite Kopie der Angestellter-Tabelle.
  • Die Bedingung F.Land = S.Land schließt die Paarbildung von Angestellten verschiedener Länder aus. Die obige Fragestellung suchte nach Angestellten im gleichen Land.
  • Die Bedingung F.AngestellterID < S.AngestellterID schließt die Paarbildungen der AngestellterIDs der ersten Angestellter-Tabelle aus, die größer oder gleich der AngestellterID der zweiten Tabelle sind. In anderen Worten: die Folge dieser Bedingung ist das Ausschließen duplizierter Paarbildungen und Selbst-Paarbildungen. Ohne sie generiert sich die folgende wenig nützliche Tabelle (nur der Ausschnitt mit Deutschland ist gezeigt):
AngestellterID Nachname AngestellterID Nachname Land
305 Weber 305 Weber Deutschland
305 Weber 306 Meyer Deutschland
306 Meyer 305 Weber Deutschland
306 Meyer 306 Meyer Deutschland

Lediglich eine der beiden mittleren Paarbildungen ist nötig, um der Fragestellung zu genügen: die oberste und unterste sind für dieses Beispiel nicht von Interesse.

Einbindung

Datenbanksystemen erfordern viel Arbeit bei der effizienten Einbindung von Joins, da relationale Systeme für gewöhnlich die Verwendung von Joins mit sich bringen. Die Schwierigkeit liegt in der Optimierung ihrer effizienten Ausführung. Das Problem entsteht, da INNER JOINs sich sowohl kommutativ als auch assoziativ verhalten. In der Praxis muss der Benutzer dadurch nur noch die Liste mit den zu joinenden Tabellen und die zu verwendende Join-Bedingung ausgeben. Das Datenbanksystem übernimmt anschließend die Aufgabe, den effizientesten Weg für die Durchführung zu bestimmen. Ein Abfrageoptimierer bestimmt dabei die Ausführungsart von Abfragen in Joins.

Ein Abfrageoptimierer hat zwei elementare Freiheiten:

  • Join-Reihenfolge: Aufgrund der kommutativen und assoziativen Eigenschaft der Join-Funktionen verändert die Abarbeitung der Reihenfolge, in der das System die Join-Tabellen abarbeitet, nicht das Ergebnis. Die Wahl einer geeigneten Join-Reihenfolge reduziert die eventuell sehr hohen Kosten einer Join-Durchführung.
  • Join-Methode: Bei zwei gegebenen Tabellen und einer Join-Bedingung können verschiedene Algorithmen das Ergebnis-Set eines Joins produzieren. Welcher Algorithmus am effizientesten läuft, hängt von der Größe der eingegebenen Tabellen, der Anzahl der Zeilen jeder Tabelle, die zur Join-Bedingung passen, und von den Arbeitsschritten der restlichen Abfrage ab.

Die Abarbeitung durch die Joinalgorithmen läuft dabei unterschiedlich ab. Die Eingaben eines Joins heißen äußere ("outer") oder innere ("inner") Operatoren, beziehungsweise links ("LEFT") und rechts ("RIGHT"). Bei verschachtelten Schleifen zum Beispiel scannt das Datenbanksystem die ganze innere Relation für jede Zeile der äußeren Relation.

Klassische Auswertungspläne die Joins enthalten:[10]

  • links-tief ("left-deep"): eine Basistabelle als innerer Operator für jeden Join des Auswertungsplans
  • rechts-tief ("right-deep"): eine Basistabelle als äußerer Operator für jeden Join des Auswertungsplans
  • bushy: weder links-tief noch rechts-tief, beide Join-Eingaben können selbst aus Joins entstehen

Die Namen leiten sich aus dem Erscheinungsbild des als Baum gezeichneten Auswertungsplans ab. Dabei ist durch Namenskonvention die äußere Relation als linke ("LEFT") Seite und die innere Relation als rechte ("RIGHT") Seite definiert.

Join-Algorithmen

Die drei fundamentalen Algorithmen für Join-Operation lauten: Nested Loop-Join, Sort-Merge Join und Hash-Join.

Join-Indizes

Join-Indizes sind Datenbankindizes, die das Verarbeiten von Join-Abfragen in Data-Warehouses ermöglichen: Sie sind seit 2012 in Oracle[11] und Teradata eingebunden.

Die Definition der Teradata-Einbindung für ausgewiesene Spalten, aggregierte Funktionen von Spalten oder Komponenten von Datenspalten einer oder mehrerer Tabellen ähnelt der Syntax einer View: Ein einzelner Join-Index erlaubt die Definition von bis zu 64 Spalten(ausdrücken). Optional mit einer Definition der Spalte des Primärschlüssels der erstellen Daten: Parallel arbeitende Hardware verwendet die Spaltenwerte für die Partition der Inhalte der Indizes über mehrere Platten. Bei interaktiven Benutzerupdates von Quelldaten updaten sich die Join-Indizes automatisch mit. Jegliche Abfrage, deren Where-Bedingung auf eine Kombination von Spalten oder Spaltenausdrücken, die im Join-Index definiert sind („Covering-Abfrage“), eingeht, führt zu einem Auslesen des Join-Indexes statt der Original-Tabellen und deren Indizes während der Ausführung.

Die Einbindung von Oracle beschränkt sich selbst durch die Verwendung des Bitmap-Indexes. Dieser indiziert und verbindet zusammengehörige niedrig-kardinale Spalten (weniger als 300 einzigartige Werten gemäß Oracle-Dokumentation). Das Beispiel von Oracle verwendet ein Bestandsverzeichnis, für welches verschiedene Zulieferer unterschiedliche Teile liefern. Das betreffende Schema hat drei verlinkte Tabellen: zwei „Master-Tabellen“ Teil ("Part") und Zulieferer ("Supplier") und eine „Detail-Tabelle“ Inventar ("Inventory"). Das letzte ist eine many-to-many-Tabelle, welche Zulieferer und Teil miteinander verbindet und die meisten Zeilen enthält. Jedes Teil hat einen Teil-Typ, jeder Zulieferer hat seinen Sitz in den USA und eine Spalte Staat ("State"). Die USA bestehen aus nicht mehr als 60 Staaten und Territorien und die Teil-Typen sind auf 300 beschränkt. Ein Join der obigen drei Tabellen definiert in der Regel den Bitmap-Join-Index und spezifiziert den Teil-Typ und den Zulieferer-Staat-Index. Er ist jedoch durch die Inventar-Tabelle definiert, selbst wenn die Spalten Teil-Typ und Zulieferer-Staat nur von Zulieferer, beziehungsweise Teil "geliehen" sind.

Genauso wie für Teradata, verwendet Oracle ein Bitmap-Join-Index nur um eine Abfrage zu beantworten, dessen Where-Bedingung auf Spalten beschränkt ist, die im Join-Index enthalten sind.

STRAIGHT_JOIN

In manchen Datenbank-Systemen kann der Benutzer das Lesen der Tabellen eines Joins in einer bestimmten Reihenfolge erzwingen. Dies ist besonders hilfreich, wenn der Abfrageoptimierer die Tabellen in einer ineffizienten Reihenfolge abarbeitet. Der Befehl STRAIGHT_JOIN in MySQL beispielsweise liest die Tabellen in exakt der gleichen Reihenfolge der Abfrageliste.[12]

Siehe auch

Weiterführende Literatur

  • Phillip J Pratt: A Guide To SQL, Seventh Edition. Thomson Course Technology, 2005, ISBN 978-0-619-21674-0.
  • Nilesh Shah: Database Systems Using Oracle – A Simplified Guide to SQL and PL/SQL Second Edition. International Auflage. Pearson Education International, 2005, ISBN 0-13-191180-5 ( [2002]).
  • Clement T. Yu, Weiyi Meng: Principles of Database Query Processing for Advanced Applications. Morgan Kaufmann, 1998, ISBN 978-1-55860-434-6 (google.com [abgerufen am 3. März 2009]).

Einzelnachweise

  1. SQLGuides: SQL CROSS JOIN. Abgerufen am 19. September 2018.
  2. Greg Robidoux: Avoid SQL Server functions in the WHERE Bedingung for Performance. In: MSSQL Tips, 5/3/2007
  3. Patrick Wolf: Caution when using PL/SQL functions in a SQL statement. Inside Oracle APEX, 11/30/2006
  4. Gregory A. Larsen: T-SQL Best Practices – Don’t Use Scalar Value Functions in Column List or WHERE Bedingungs. 10/29/2009,
  5. Java2s: Simplifying Joins with the USING Keyword. Abgerufen am 19. September 2018.
  6. AskTom: Oracle support of ANSI joins. Abgerufen am 18. September 2018.
  7. DBA Oracle: Left Outer Join. Abgerufen am 19. September 2018.
  8. Nilesh Shah: Database Systems Using Oracle – A Simplified Guide to SQL and PL/SQL. Second Edition (International ed.). Pearson Education International, (2005) [2002], S. 165
  9. Entnommen aus: Phillip J. Pratt: A Guide To SQL. Seventh Edition. Thomson Course Technology, 2005, S. 115–116
  10. Clement T. Yu, Weiyi Meng: Principles of Database Query Processing for Advanced Applications. 1998, S. 213
  11. DBA Oracle: Oracle Bitmap Join Index. Abgerufen am 19. September 2018.
  12. Oracle Corporation: MySQL 5.7 Reference Manual:JOIN Syntax. Abgerufen am 19. September 2018.

Vorlage:SQL