CodeGym /Kursy /SQL SELF /Dodatkowe warunki w JOIN: ON ... AND...

Dodatkowe warunki w JOIN: ON ... AND ...

SQL SELF
Poziom 12, Lekcja 2
Dostępny

Już wiesz, jak łączyć tabele za pomocą JOIN. Ale w prawdziwym życiu zwykłe dopasowanie po kluczach często nie wystarcza. Często pojawia się zadanie: łączyć dane tylko jeśli spełniają dodatkowe kryterium — na przykład tylko aktywne rekordy, tylko dane z bieżącego roku albo tylko zakończone zamówienia.

I tu właśnie wchodzi rozszerzenie konstrukcji ON z użyciem AND.

Dodatkowe warunki w JOIN ... ON pozwalają dokładnie kontrolować, które wiersze biorą udział w łączeniu, zanim SQL zacznie budować wynik. Dzięki temu zapytanie jest:

  • Szybsze (mniej wierszy przechodzi przez JOIN),
  • Dokładniejsze (filtrowanie odbywa się już na etapie łączenia),
  • Bardziej przewidywalne przy pracy z LEFT JOIN (w przeciwieństwie do filtrowania w WHERE).

Przykład: Tylko aktywne zapisy na kursy

Załóżmy, że tabela enrollments zawiera status uczestnictwa studenta: active, dropped, pending.

Tabela students:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Zaktualizowana tabela enrollments:

student_id course_id status
1 101 active
1 103 active
2 102 dropped
3 101 active

Tabela courses:

id name
101 Mathematics
102 Physics
103 Computer Science

Teraz chcemy uzyskać tylko tych studentów, którzy mają aktywne kursy:

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;

Wynik:

student_name course_name
Otto Song Mathematics
Otto Song Computer Science
Alex Lin Mathematics

Tutaj dodaliśmy AND enrollments.status = 'active' do środka ON, żeby łączenie odbywało się tylko po aktywnych rekordach, a nie filtrowało się po połączeniu.

Dlaczego nie WHERE?

Można by napisać tak:

...
WHERE enrollments.status = 'active'

Ale to ma inne zachowanie przy LEFT JOIN. Filtrowanie w WHERE usuwa wiersze, gdzie nie ma dopasowań (NULL), przez co zamienia LEFT JOIN w INNER JOIN.

A warunek AND enrollments.status = 'active' wewnątrz ON od razu ogranicza łączone wiersze — steruje tym, które wiersze w ogóle trafią do połączenia, a nie tylko filtruje wynik po fakcie.

To podejście jest szczególnie ważne, jeśli chcesz zachować wiersze z jednej tabeli, nawet jeśli w drugiej nie ma pasujących wartości (co często się zdarza w raportach i analizie).

Kolejne przykłady użycia ON ... AND ...

Tabela students:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Tabela 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

Tabela courses:

id name
101 Math
102 Physics
103 CS

Przykład: tylko kursy z bieżącego roku

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;

Tutaj łączymy tylko te rekordy, które dotyczą bieżącego roku.

name name enrolled_at
Otto Song Math 2025-02-01
Otto Song CS 2025-03-05
Alex Lin Math 2025-03-12

Przykład: wykluczenie po wartości

JOIN enrollments
    ON students.id = enrollments.student_id
    AND enrollments.status != 'dropped'

Wykluczamy studentów, którzy się wypisali, już na etapie łączenia, a nie filtrujemy po fakcie.

name name
Otto Song Math
Otto Song CS
Alex Lin Math

Kiedy warunek jest wewnątrz ON, PostgreSQL może optymalizować plan łączenia i przetwarzać mniej wierszy. To szczególnie ważne przy dużych ilościach danych. Wewnętrzne filtrowanie jest wydajniejsze niż "odsiew" po JOIN.

JOIN ON — to nie tylko klucze

Wielu myśli, że ON to po prostu id = id. Tak naprawdę można tam wrzucać:

  • Operatory logiczne: AND, OR, NOT
  • Porównania: >, <, <>, BETWEEN, IN
  • Wyrażenia: EXTRACT, DATE_TRUNC, COALESCE, NULLIF

Łączymy wszystko razem

Tabela students:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Tabela faculties:

id name
10 Engineering
20 Natural Sciences
30 ← bez nazwy (NULL)

Tabela 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

Tabela 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 != 'PhysicalEducation'
JOIN faculties f
    ON c.faculty_id = f.id
    AND f.name IS NOT NULL;

Tutaj jednocześnie filtrujemy po:

  • Aktywnych rekordach,
  • Kursach innych niż "PhysicalEducation",
  • Wydziałach, które mają nazwę.

Wynik zapytania:

student_name course_name faculty_name
Otto Song Math Engineering
Otto Song CS Engineering
Alex Lin Math Engineering

Mam nadzieję, że spodobał ci się ten wykład. Bardzo często będziesz używać w zapytaniach kilku JOIN z filtrami. Prawie zawsze :)

2
Zadanie
SQL SELF, poziom 12, lekcja 2
Niedostępne
Wybór studentów z aktywnymi kursami
Wybór studentów z aktywnymi kursami
2
Zadanie
SQL SELF, poziom 12, lekcja 2
Niedostępne
Wybór rekordów za bieżący rok
Wybór rekordów za bieżący rok
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION