Ein Artikel von Jeff Atwood in seinem Blog „Coding Horror“ sowie zwei passgenau zum gleichen Zeitpunkt sich ereignende programmiertechnische Episoden reizen mich, mal eine Lanze für Joins in SQL-Statements zu brechen. Joins sind eine schöne Sache und eigentlich nicht sehr schwierig. Aktuelle RDBMS kommen prima mit Joins aus, also gibt es Gründe, auf Joins zu verzichten?
Beginnen wir mal mit zwei Tabellen, um die Vergleiche zu beginnen. Als Thema bewege ich mich mal auf glühende Kohlen, also ab in die Politik!
[sourcecode language=“sql“]
CREATE TABLE `spd_politiker` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(40) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB; [/sourcecode]
[sourcecode language=“sql“]
CREATE TABLE `minister` (
`ID` int(11) NOT NULL,
`Name` varchar(40) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB; [/sourcecode]
….und zum Spielen nehmen wir auch noch ein paar Daten aus dem harten Berliner Politikalltag…
Tabelle Minister: Tabelle SPD-Politiker ID Name ID Name -- ---- -- ---- 1 Frank-Walter Steinmeier 1 Franz Müntefering 2 Wolfgang Schäuble 2 Frank-Walter Steinmeier 3 Brigitte Zypries 3 Brigitte Zypries 4 Peer Steinbrück 4 Peer Steinbrück 5 Michael Glos 5 Ulla Schmidt 6 Franz Müntefering 6 Wolfgang Tiefensee 7 Horst Seehofer 7 Sigmar Gabriel 8 Franz Josef Jung 8 Willy Brandt 9 Ursula von der Leyen 9 Helmut Schmidt 10 Ulla Schmidt 10 Gerhard Schröder 11 Wolfgang Tiefensee 11 Kurt Beck 12 Sigmar Gabriel 12 Johannes Rau 13 Annette Schavan
Ran an die SQL-Joins…
1. INNER JOIN
Ein inner join liefert im Prinzip die Schnittmenge aus beiden Grundmengen. Für das konstruierte Beispiel aus der Politik würden also alle Minister ausgegeben werden, welche gleichzeitig SPD-Politiker sind bzw. waren.
[sourcecode language=“sql“]
select * from minister
inner join spd_politiker on minister.Name = spd_politiker.Name;
[/sourcecode]
ID | Name | ID | Name |
1 | Frank-Walter Steinmeier | 2 | Frank-Walter Steinmeier |
3 | Brigitte Zypries | 3 | Brigitte Zypries |
4 | Peer Steinbrück | 4 | Peer Steinbrück |
6 | Franz Müntefering | 1 | Franz Müntefering |
10 | Ulla Schmidt | 5 | Ulla Schmidt |
11 | Wolfgang Tiefensee | 6 | Wolfgang Tiefensee |
12 | Sigmar Gabriel | 7 | Sigmar Gabriel |
In diesem Fall kann man das gewünschte Ergebnis noch etwas einfacher erhalten, jedoch behandelt dieser Artikel das Verhalten von Joins…!
2. LEFT (OUTER) JOIN
Ein left outer join liefert als Ergebnis alle Datensätze der ersten Tabelle sowie alle passenden Datensätze von Tabelle 2. Hier werden somit alle Minister ausgegeben, als Ergänzung erscheinen die Daten aus der SPD-Politiker-Tabelle, falls ein Minister dort enthalten sein sollte.
[sourcecode language=“sql“]
select * from minister
left outer join spd_politiker
on minister.Name = spd_politiker.Name;
[/sourcecode]
ID | Name | ID | Name |
1 | Frank-Walter Steinmeier | 2 | Frank-Walter Steinmeier |
2 | Wolfgang Schäuble | (NULL) | (NULL) |
3 | Brigitte Zypries | 3 | Brigitte Zypries |
4 | Peer Steinbrück | 4 | Peer Steinbrück |
5 | Michael Glos | (NULL) | (NULL) |
6 | Franz Müntefering | 1 | Franz Müntefering |
7 | Horst Seehofer | (NULL) | (NULL) |
8 | Franz Josef Jung | (NULL) | (NULL) |
9 | Ursula von der Leyen | (NULL) | (NULL) |
10 | Ulla Schmidt | 5 | Ulla Schmidt |
11 | Wolfgang Tiefensee | 6 | Wolfgang Tiefensee |
12 | Sigmar Gabriel | 7 | Sigmar Gabriel |
13 | Annette Schavan | (NULL) | (NULL) |
Häufig verwende ich diese left outer joins, um Datensätze zu finden, welche nicht in der anderen Tabelle vorhanden sind. Hierfür kann man die SQL-Abfrage leicht modifizieren – das Ergebnis ist in diesem Falle alle Nicht-SPD-Minister:
[sourcecode language=“sql“]
select * from minister
left outer join spd_politiker
on minister.Name = spd_politiker.Name
where spd_politiker.ID IS NULL;
[/sourcecode]
3. RIGHT (OUTER) JOIN
Ein right outer join funktioniert eigentlich genauso wie ein left outer join liefert als Ergebnis alle Datensätze der zweiten Tabelle sowie alle passenden Datensätze von Tabelle 1. Hier werden somit alle SPD-Politiker ausgegeben, als Ergänzung erscheinen die Daten aus der Minister-Tabelle, falls der Politiker dort auch ein Amt bekleidet.
[sourcecode language=“sql“]
select * from minister
right outer join spd_politiker
on minister.Name = spd_politiker.Name;
[/sourcecode]
ID | Name | ID | Name |
6 | Franz Müntefering | 1 | Franz Müntefering |
1 | Frank-Walter Steinmeier | 2 | Frank-Walter Steinmeier |
3 | Brigitte Zypries | 3 | Brigitte Zypries |
4 | Peer Steinbrück | 4 | Peer Steinbrück |
10 | Ulla Schmidt | 5 | Ulla Schmidt |
11 | Wolfgang Tiefensee | 6 | Wolfgang Tiefensee |
12 | Sigmar Gabriel | 7 | Sigmar Gabriel |
(NULL) | (NULL) | 8 | Willy Brandt |
(NULL) | (NULL) | 9 | Helmut Schmidt |
(NULL) | (NULL) | 10 | Gerhard Schröder |
(NULL) | (NULL) | 11 | Kurt Beck |
(NULL) | (NULL) | 12 | Johannes Rau |
Auch mit dem right outer joins kann man wunderbar die IS NULL-Eigenschaft verwenden, um Datensätze zu finden, welche nicht in der anderen Tabelle vorhanden sind.
4. FULL OUTER JOIN
Ein full outer join gibt eigentlich alle Datensätze von Tabelle A und Tabelle B aus, wobei die Schnittmenge zusammengefasst wird. Ein paar RDBMS, wie z.B. auch MySQL, haben keine direkte Unterstützung des full outer join, jedoch kann man ohne viel Umstand SQL-Konstrukte basteln, die dies imitieren – schließlich ist das ja nichts anderes wie eine Zusammenfassung des left outer join und des right outer join.
Hier ein Beispiel eines Workarounds für MySQL mit Verwendung von union all:
[sourcecode language=“sql“]
select * from minister
left outer join spd_politiker
on minister.Name = spd_politiker.Name
union all
select * from minister
right outer join spd_politiker
on minister.Name = spd_politiker.Name
where minister.Name is null;
[/sourcecode]
ID | Name | ID | Name |
1 | Frank-Walter Steinmeier | 2 | Frank-Walter Steinmeier |
2 | Wolfgang Schäuble | (NULL) | (NULL) |
3 | Brigitte Zypries | 3 | Brigitte Zypries |
4 | Peer Steinbrück | 4 | Peer Steinbrück |
5 | Michael Glos | (NULL) | (NULL) |
6 | Franz Müntefering | 1 | Franz Müntefering |
7 | Horst Seehofer | (NULL) | (NULL) |
8 | Franz Josef Jung | (NULL) | (NULL) |
9 | Ursula von der Leyen | (NULL) | (NULL) |
10 | Ulla Schmidt | 5 | Ulla Schmidt |
11 | Wolfgang Tiefensee | 6 | Wolfgang Tiefensee |
12 | Sigmar Gabriel | 7 | Sigmar Gabriel |
13 | Annette Schavan | (NULL) | (NULL) |
(NULL) | (NULL) | 8 | Willy Brandt |
(NULL) | (NULL) | 9 | Helmut Schmidt |
(NULL) | (NULL) | 10 | Gerhard Schröder |
(NULL) | (NULL) | 11 | Kurt Beck |
(NULL) | (NULL) | 12 | Johannes Rau |
Mehr Methoden, um einen full outer join mit MySQL nachzubauen, habe ich hier gefunden.
Anmerkung:
Wie der Kommentator Steffen korrekterweise moniert, ist einJOIN
tatsächlich keine Schnittmenge, sondern vielmehr ein kartesisches Produkt in Verbindung mit einer Selektion. Datenbanktheoretiker mögen mich strafen, aber ich bleibe dennoch bei meiner starken Vereinfachung in diesem Artikel!
Schreibe einen Kommentar