Die Angst des Programmierers vor dem SQL-Join

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!

CREATE TABLE `spd_politiker` (           
   `ID` int(11) NOT NULL AUTO_INCREMENT,  
   `Name` varchar(40) DEFAULT NULL,       
   PRIMARY KEY (`ID`)                     
) ENGINE=InnoDB; 

CREATE TABLE `minister` (               
   `ID` int(11) NOT NULL,                
   `Name` varchar(40) DEFAULT NULL,      
   PRIMARY KEY (`ID`)                    
) ENGINE=InnoDB; 

….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

Inner JoinEin 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.

select * from minister
inner join spd_politiker on minister.Name = spd_politiker.Name;
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

Left JoinEin 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.

select * from minister
left outer join spd_politiker 
    on minister.Name = spd_politiker.Name;
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:

select * from minister
left outer join spd_politiker 
    on minister.Name = spd_politiker.Name
where spd_politiker.ID IS NULL;

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.

select * from minister
right outer join spd_politiker 
    on minister.Name = spd_politiker.Name;
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
Full Outer JoinEin 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:

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;
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 ein JOIN 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!