Dies ist eine alte Version des Dokuments!
LU10a - SELECT über mehrere Tabellen
Lernziele
- Ich kann mit
INNER JOIN … ONDaten aus zwei und drei Tabellen gemeinsam abfragen. - Ich kann die ältere Schreibweise mit
FROMundWHERElesen und erklären, was ohne Verknüpfungsbedingung passiert. - Ich kann Aliase für Tabellen und Spalten verwenden.
- Ich weiss, welche Bedingung in
ONgehört und welche inWHERE.
Vorbereitung: Datenbank laden
Für diese Seite brauchen Sie die Datenbank travel_blog aus LU08 mit den Tabellen users, posts, categories und post_category. Das Skript legt sie mit allen Beispieldaten an.
- Skript herunterladen: Travel-Blog-Datenbank (SQL-Skript, gezippt)
- Die ZIP-Datei entpacken.
- In WebStorm im Datenbank-Fenster mit der rechten Maustaste auf die Verbindung klicken (z.B. mysql@localhost).
- SQL Scripts > Run SQL Script… wählen und die entpackte .sql-Datei auswählen.
- Danach nochmals mit der rechten Maustaste auf die Verbindung klicken, Tools > Manage Shown Schemas… wählen und travel_blog ankreuzen.
Screencast: SQL-Skript in WebStorm laden.
1) Warum mehrere Tabellen abfragen?
Video: Abfragen über mehrere Tabellen.
In unserem Reiseblog sind die Daten auf vier Tabellen verteilt:
- users: die Autorinnen und Autoren
- posts: die Beiträge, mit
author_idals Verweis auf users - categories: die Kategorien
- post_category: welcher Post zu welcher Kategorie gehört
Bisher haben wir immer nur eine Tabelle abgefragt:
SELECT post_id, title, author_id FROM posts WHERE post_id = 1;
| post_id | title | author_id |
|---|---|---|
| 1 | Hasselt – 10 Highlights | 2 |
Für die Website reicht das nicht. Dort soll beim Post der Name der Autorin oder des Autors stehen, nicht die Nummer 2. Der Name steht aber in einer anderen Tabelle, in users in der Spalte display_name. Wir müssen also posts und users gemeinsam abfragen.
2) Zwei Tabellen verbinden
SELECT p.post_id, p.title, u.display_name AS author FROM posts AS p INNER JOIN users AS u ON p.author_id = u.user_id;
So ist die Abfrage aufgebaut:
| Teil | Bedeutung |
|---|---|
FROM posts | Die Tabelle, deren Zeilen wir auflisten wollen |
INNER JOIN users | Die Tabelle, aus der wir zusätzliche Angaben holen |
ON p.author_id = u.user_id | Wie die beiden Tabellen zusammenhängen |
Die Bedingung nach ON verbindet den Fremdschlüssel der Kindtabelle 🧒 posts mit dem Primärschlüssel der Elterntabelle 👩🦳👨🏼🦰 users. Das ist genau die Verknüpfung, die Sie in LU08 mit FOREIGN KEY angelegt haben.
Resultat:
| post_id | title | author |
|---|---|---|
| 1 | Hasselt – 10 Highlights | Martin Merten |
| 2 | Utrecht – 10 Sehenswürdigkeiten | Martin Merten |
| 3 | Lissabon – 8 Tipps zu den wichtigsten Sehenswürdigkeiten | Caro Steig |
| 4 | Maastricht an einem Tag | Caro Steig |
| 5 | Montenegro Roadtrip – 10 Highlights | Caro Steig |
| 6 | Oman – Top 22 Highlights | Caro Steig |
| 7 | Chicago in 3 Tagen – 17 Highlights | Martin Merten |
Aliase
Ein Alias ist ein Kurzname. Es gibt zwei Arten:
- Tabellenalias:
FROM posts AS pbedeutet, dass posts im Statementpheisst. Das spart Schreibarbeit. - Spaltenalias:
u.display_name AS authorgibt der Spalte im Resultat den Namen author.
Das Wort AS ist optional. FROM posts p funktioniert genauso.
Hat eine Tabelle einen Alias, müssen Sie ihn im ganzen Statement verwenden, auch in SELECT, ON und WHERE. Den vollen Tabellennamen kennt MySQL dann nicht mehr:
SELECT posts.title FROM posts AS p; → Unknown column 'posts.title' in 'field list'
Die ältere Schreibweise mit WHERE
Sie werden in älteren Beispielen und im Internet oft diese Variante sehen. Sie liefert dasselbe Resultat:
SELECT posts.post_id, posts.title, users.display_name FROM posts, users WHERE posts.author_id = users.user_id;
Hier stehen beide Tabellen nach FROM, und die Verknüpfung steht in WHERE. Wir verwenden in diesem Modul INNER JOIN, weil Verknüpfung und Filter so klar getrennt sind.
Vergessen Sie die Bedingung, kombiniert MySQL jede Zeile mit jeder. SELECT * FROM posts, users; liefert 21 Zeilen statt 7: jeden der 7 Posts mit jedem der 3 User. Das Resultat sieht vollständig aus, ist aber falsch.
3) Drei Tabellen: n:m über die Zwischentabelle
Video: Abfragen über eine n:m-Beziehung.
Posts und Kategorien hängen über die Zwischentabelle post_category zusammen. Die Abfrage geht deshalb in zwei Schritten: von posts zu post_category und von dort weiter zu categories.
SELECT p.title, c.name AS category FROM posts p INNER JOIN post_category pc ON p.post_id = pc.post_id INNER JOIN categories c ON pc.category_id = c.category_id ORDER BY p.post_id, c.name;
Resultat (Ausschnitt):
| title | category |
|---|---|
| Hasselt – 10 Highlights | Belgien |
| Hasselt – 10 Highlights | Städtereise |
| Utrecht – 10 Sehenswürdigkeiten | Niederlande |
| Utrecht – 10 Sehenswürdigkeiten | Städtereise |
| Lissabon – 8 Tipps zu den wichtigsten Sehenswürdigkeiten | Portugal |
| Lissabon – 8 Tipps zu den wichtigsten Sehenswürdigkeiten | Städtereise |
Für jede Kategorie entsteht eine eigene Zeile. Ein Post mit drei Kategorien erscheint deshalb dreimal. Das ist richtig so.
Mit Filter: die Kategorien eines bestimmten Posts
Wie im Video: Welche Kategorien hat der Post Utrecht (post_id 2)?
SELECT c.name AS category FROM posts p INNER JOIN post_category pc ON p.post_id = pc.post_id INNER JOIN categories c ON pc.category_id = c.category_id WHERE p.post_id = 2;
| category |
|---|
| Niederlande |
| Städtereise |
4) Worauf Sie achten müssen
Welche Tabelle steht nach FROM?
Bei INNER JOIN ist das für das Resultat egal. Ob Sie bei posts oder bei categories beginnen, es kommen dieselben Zeilen zurück. Für die Lesbarkeit hilft eine einfache Regel: Nach FROM steht die Tabelle, deren Zeilen Sie auflisten wollen. Von dort aus folgen Sie mit INNER JOIN den Fremdschlüsseln:
- 1:n: posts → users
- n:m: posts → post_category → categories
ON oder WHERE?
| Klausel | Wofür | Beispiel |
|---|---|---|
ON | Wie die Tabellen zusammenhängen | ON p.author_id = u.user_id |
WHERE | Welche Zeilen Sie sehen wollen | WHERE p.post_id = 2 |
ORDER BY | In welcher Reihenfolge | ORDER BY p.title |
Gleichnamige Spalten
posts und post_category haben beide eine Spalte post_id. Schreiben Sie in SELECT nur post_id, weiss MySQL nicht, welche gemeint ist:
Column 'post_id' in field list is ambiguous
Die Lösung: Stellen Sie den Tabellennamen oder den Alias davor, also p.post_id. Am einfachsten ist es, das bei Abfragen über mehrere Tabellen immer zu tun.
Zeilen ohne Partner fehlen
INNER JOIN zeigt nur Zeilen, die in beiden Tabellen einen passenden Eintrag haben. Die Kategorie Deutschland ist keinem Post zugeordnet. Sie fehlt deshalb im Resultat der n:m-Abfrage, obwohl sie in categories steht.
Zusammenfassung
SELECT a.spalte, b.spalte, c.spalte FROM tabelle_a a INNER JOIN tabelle_b b ON a.spalte = b.spalte -- Fremdschlüssel = Primärschlüssel INNER JOIN tabelle_c c ON b.spalte = c.spalte -- bei Bedarf weitere Tabellen WHERE ... -- welche Zeilen ORDER BY ...; -- in welcher Reihenfolge



