JavaRush /Курси /SQL SELF /Додаткові умови в JOIN: ON ... AND ....

Додаткові умови в JOIN: ON ... AND ...

SQL SELF
Рівень 12 , Лекція 2
Відкрита

Ти вже знаєш, як об'єднувати таблиці за допомогою JOIN. Але в реальному житті простого співпадіння по ключах може бути замало. Часто виникає задача: об'єднувати дані, тільки якщо вони відповідають додатковому критерію — наприклад, тільки активні записи, тільки дані за поточний рік або тільки завершені замовлення.

І ось тут у гру вступає розширення конструкції ON за допомогою AND.

Додаткові умови в JOIN ... ON дозволяють точно контролювати, які рядки беруть участь у з'єднанні, ще до того, як SQL почне будувати результат. Це робить запит:

  • Швидшим (менше рядків проходить через JOIN),
  • Точнішим (фільтрація відбувається на етапі з'єднання),
  • Передбачуванішим при роботі з LEFT JOIN (на відміну від фільтрації у WHERE).

Приклад: Тільки активні записи на курси

Припустимо, таблиця enrollments містить статус участі студента: active, dropped, pending.

Таблиця students:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Оновимо таблицю enrollments:

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

Таблиця courses:

id name
101 Mathematics
102 Physics
103 Computer Science

Тепер ми хочемо отримати тільки тих студентів, у яких активні курси:

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;

Результат:

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

Тут ми додали AND enrollments.status = 'active' всередину ON, щоб з'єднання відбувалося тільки по активних записах, а не фільтрувалося після об'єднання.

Чому не WHERE?

Можна було б написати так:

...
WHERE enrollments.status = 'active'

Але це має іншу поведінку при LEFT JOIN. Фільтрація у WHERE видаляє рядки, де немає співпадінь (NULL), і таким чином перетворює LEFT JOIN на INNER JOIN.

А ось умова AND enrollments.status = 'active' всередині ON одразу обмежує самі з'єднувані рядки — керує тим, які рядки взагалі потраплять у з'єднання, а не просто фільтрує результат після.

Такий підхід особливо важливий, якщо ти хочеш зберегти рядки з однієї таблиці, навіть якщо в іншій таблиці немає підходящих значень (що часто буває у звітах та аналітиці).

Ще приклади використання ON ... AND ...

Таблиця students:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Таблиця 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

Таблиця courses:

id name
101 Math
102 Physics
103 CS

Приклад: тільки курси поточного року

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;

Тут ми об'єднуємо тільки ті записи, які стосуються поточного року.

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

Приклад: виключення за значенням

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

Виключаємо студентів, які відраховані, на етапі з'єднання, а не фільтруємо після.

name name
Otto Song Math
Otto Song CS
Alex Lin Math

Коли умова розміщена всередині ON, PostgreSQL може оптимізувати план з'єднання і обробити менше рядків. Це особливо критично на великих об'ємах даних. Внутрішня фільтрація ефективніша, ніж "відсів" після JOIN.

JOIN ON — це не тільки ключі

Багато хто думає, що ON — це просто id = id. Насправді туди можна вставляти:

  • Логічні оператори: AND, OR, NOT
  • Порівняння: >, <, <>, BETWEEN, IN
  • Вирази: EXTRACT, DATE_TRUNC, COALESCE, NULLIF

Комбінуємо все разом

Таблиця students:

id name
1 Otto Song
2 Maria Chi
3 Alex Lin

Таблиця faculties:

id name
10 Engineering
20 Natural Sciences
30 ← без імені (NULL)

Таблиця 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

Таблиця 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 != 'Фізкультура'
JOIN faculties f
    ON c.faculty_id = f.id
    AND f.name IS NOT NULL;

Тут ми одночасно фільтруємо по:

  • Активних записах,
  • Курсах, окрім "Фізкультури",
  • Факультетах, у яких є ім'я.

Результат виконання запиту:

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

Сподіваюсь, тобі сподобалась ця лекція. Ти будеш дуже часто використовувати у запитах кілька JOIN з фільтрами. Майже завжди :)

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ