Zusätzliche Bedingungen bei JOIN: ON ... AND ...
Du weißt schon, wie man Tabellen mit JOIN verbindet. Aber im echten Leben reicht einfaches Matchen nach Keys oft nicht aus. Oft brauchst du: Daten verbinden nur wenn sie noch einem weiteren Kriterium entsprechen – zum Beispiel nur aktive Einträge, nur Daten aus dem aktuellen Jahr oder nur abgeschlossene Bestellungen.
Und genau hier kommt die Erweiterung von ON mit AND ins Spiel.
Zusätzliche Bedingungen im JOIN ... ON erlauben dir ganz genau zu steuern, welche Zeilen beim Joinen dabei sind, noch bevor SQL das Ergebnis baut. Das macht die Abfrage:
- Schneller (weniger Zeilen gehen durch den
JOIN), - Genauer (gefiltert wird schon beim Joinen),
- Vorhersehbarer bei
LEFT JOIN(anders als beim Filtern imWHERE).
Beispiel: Nur aktive Kursanmeldungen
Nehmen wir an, die Tabelle enrollments enthält den Status der Teilnahme eines Studenten: active, dropped, pending.
Tabelle students:
| id | name |
|---|---|
| 1 | Otto Song |
| 2 | Maria Chi |
| 3 | Alex Lin |
Aktualisierte Tabelle enrollments:
| student_id | course_id | status |
|---|---|---|
| 1 | 101 | active |
| 1 | 103 | active |
| 2 | 102 | dropped |
| 3 | 101 | active |
Tabelle courses:
| id | name |
|---|---|
| 101 | Mathematics |
| 102 | Physics |
| 103 | Computer Science |
Jetzt wollen wir nur die Studenten mit aktiven Kursen bekommen:
SELECT
students.name AS student_name,
courses.name AS course_name
FROM students
INNER JOIN enrollments
ON students.id = enrollments.student_id
AND enrollments.status = 'active'
INNER JOIN courses
ON enrollments.course_id = courses.id;
Ergebnis:
| student_name | course_name |
|---|---|
| Otto Song | Mathematics |
| Otto Song | Computer Science |
| Alex Lin | Mathematics |
Hier haben wir AND enrollments.status = 'active' in das ON gepackt, damit das Joinen nur für aktive Einträge passiert und nicht erst nach dem Join gefiltert wird.
Warum nicht WHERE?
Man könnte auch so schreiben:
...
WHERE enrollments.status = 'active'
Aber das verhält sich anders bei LEFT JOIN. Das Filtern im WHERE schmeißt Zeilen ohne Match (NULL) raus und macht so aus dem LEFT JOIN quasi einen INNER JOIN.
Das AND enrollments.status = 'active' im ON limitiert direkt die Zeilen, die gejoint werden – du steuerst, was überhaupt ins Join kommt, nicht nur das Endergebnis.
Das ist besonders wichtig, wenn du Zeilen aus einer Tabelle behalten willst, auch wenn es in der anderen keinen passenden Wert gibt (passiert oft bei Reports und Analytics).
Noch mehr Beispiele für ON ... AND ...
Tabelle students:
| id | name |
|---|---|
| 1 | Otto Song |
| 2 | Maria Chi |
| 3 | Alex Lin |
Tabelle enrollments:
| student_id | course_id | status | enrolled_at |
|---|---|---|---|
| 1 | 101 | active | 2025-02-01 |
| 1 | 103 | active | 2025-03-05 |
| 2 | 102 | dropped | 2024-05-15 |
| 3 | 101 | active | 2025-03-12 |
Tabelle courses:
| id | name |
|---|---|
| 101 | Math |
| 102 | Physics |
| 103 | CS |
Beispiel: nur Kurse aus dem aktuellen Jahr
SELECT
students.name,
courses.name,
enrollments.enrolled_at
FROM students
JOIN enrollments
ON students.id = enrollments.student_id
AND EXTRACT(YEAR FROM enrollments.enrolled_at) = EXTRACT(YEAR FROM CURRENT_DATE)
JOIN courses
ON enrollments.course_id = courses.id;
Hier joinen wir nur die Einträge, die zum aktuellen Jahr gehören.
| name | name | enrolled_at |
|---|---|---|
| Otto Song | Math | 2025-02-01 |
| Otto Song | CS | 2025-03-05 |
| Alex Lin | Math | 2025-03-12 |
Beispiel: Ausschluss nach Wert
JOIN enrollments
ON students.id = enrollments.student_id
AND enrollments.status != 'dropped'
Wir schließen Studenten, die rausgeflogen sind, schon beim Joinen aus, nicht erst danach.
| name | name |
|---|---|
| Otto Song | Math |
| Otto Song | CS |
| Alex Lin | Math |
Wenn die Bedingung im ON steht, kann PostgreSQL den Join-Plan optimieren und weniger Zeilen verarbeiten. Das ist bei großen Datenmengen besonders wichtig. Internes Filtern ist effizienter als "Aussieben" nach dem JOIN.
JOIN ON — das sind nicht nur Keys
Viele denken, ON ist einfach nur id = id. Aber du kannst da auch reinschreiben:
- Logische Operatoren:
AND,OR,NOT - Vergleiche:
>,<,<>,BETWEEN,IN - Ausdrücke:
EXTRACT,DATE_TRUNC,COALESCE,NULLIF
Alles kombinieren
Tabelle students:
| id | name |
|---|---|
| 1 | Otto Song |
| 2 | Maria Chi |
| 3 | Alex Lin |
Tabelle faculties:
| id | name | |
|---|---|---|
| 10 | Engineering | |
| 20 | Natural Sciences | |
| 30 | ← ohne Name (NULL) |
Tabelle courses:
| id | name | teacher | faculty_id |
|---|---|---|---|
| 101 | Math | Liam Park | 10 |
| 102 | Physics | Chloe Zhang | 20 |
| 103 | CS | Noah Kim | 10 |
| 104 | PE | Ava Chen | 30 |
Tabelle enrollments:
| student_id | course_id | status |
|---|---|---|
| 1 | 101 | active |
| 1 | 103 | active |
| 2 | 102 | dropped |
| 3 | 101 | active |
| 3 | 104 | active |
SELECT
s.name AS student_name,
c.name AS course_name,
f.name AS faculty_name
FROM students s
JOIN enrollments e
ON s.id = e.student_id
AND e.status = 'active'
JOIN courses c
ON e.course_id = c.id
AND c.name != 'PE'
JOIN faculties f
ON c.faculty_id = f.id
AND f.name IS NOT NULL;
Hier filtern wir gleichzeitig nach:
- Aktiven Einträgen,
- Kursen außer "PE",
- Fakultäten, die einen Namen haben.
Ergebnis der Abfrage:
| student_name | course_name | faculty_name |
|---|---|---|
| Otto Song | Math | Engineering |
| Otto Song | CS | Engineering |
| Alex Lin | Math | Engineering |
Ich hoffe, dir hat diese Vorlesung gefallen. Du wirst in deinen Abfragen sehr oft mehrere JOINs mit Filtern benutzen. Fast immer :)
GO TO FULL VERSION