Ти вже знаєш, як об'єднувати таблиці за допомогою 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 з фільтрами. Майже завжди :)
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ