JavaRush /Курсы /SQL SELF /Дополнительные условия в JOIN: ON .....

Дополнительные условия в 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

Примечание: у факультета с id=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 != 'PE'
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 с фильтрами. Почти всегда :)

2
Задача
SQL SELF, 12 уровень, 2 лекция
Недоступна
Выбор студентов с активными курсами
Выбор студентов с активными курсами
2
Задача
SQL SELF, 12 уровень, 2 лекция
Недоступна
Выбор записей за текущий год
Выбор записей за текущий год
Комментарии (8)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
TAPTAPAbl Уровень 14
9 июня 2026
Задача не прошла тестирование Добавить DISTINCT или GROUP BY, чтобы результат содержал уникальные имена сотрудников. зачем?) и так всё вывелось и всё уникально в чем смысл выводи двух условных Иванов как одного? если это могут быть разные Иваны
Анатолий Уровень 63
29 января 2026
❤️
Jh-007 Уровень 28
25 января 2026
То left join, то inner, потом просто join. Лекция точно не понравилась.
Сергей Третяк Уровень 14
28 октября 2025
задачи, созданные для 2023 года до сих пор не скорректированы, чтобы увидеть результат решения задачи требуется менять даты в таблице БД, иначе фильтровать результат с функцией 'current_date' не имеет смысла.
Anemon Уровень 13 Expert
12 сентября 2025
🤓
Slevin Уровень 59
5 сентября 2025
Почему не WHERE? Можно было бы написать так: Потому что пример дан для INNER JOIN, а рассказывается тема про LEFT JOIN.... 😶
Anonymous #3080756 Уровень 45
19 июля 2025
На 19.07.2025 в плагине IDE задача "Выбор записей за текущий год" почему-то требует запятую перед AND, и только так выводит результат, иначе выдает синтаксическую ошибку. Но такое решение неверно и, соответственно, не проходит валидацию. В браузере решение не требует этой лишней запятой и решение хорошо проходит валидацию
25 июня 2025
На самом деле в PostgreSQL для обычного inner join все равно где находиться условие фильтрации - в теле ON или в теле WHERE, результат всегда будет одинаковый, и время выполнения и план выполнения тоже будут одинаковыми, это можно посмотреть в плане запроса. PostgreSQL сначала отфильтровывает записи (если это условие фильтрации по одной таблице), а уже потом выполняет inner join Поэтому с точки зрения планировщика, все ниже описанные запросы одинаковые

SELECT * FROM attachment_users au
JOIN attachment_users_info aui ON au.attachment_id = aui.attachment_id
WHERE aui.name = 'name61779';

SELECT * FROM attachment_users au
JOIN attachment_users_info aui ON au.attachment_id = aui.attachment_id AND aui.name = 'name61779';

SELECT * FROM attachment_users au
JOIN attachment_users_info aui ON TRUE
WHERE au.attachment_id = aui.attachment_id
AND aui.name = 'name61779';

SELECT * FROM attachment_users au
CROSS JOIN attachment_users_info aui
WHERE au.attachment_id = aui.attachment_id
AND aui.name = 'name61779'
Поэтому условия объединения 2 таблиц лучше писать в теле ON, а условия отбора по таблицам в теле WHERE, это имеет удобночитаемый и понятный вид, и на оптимизацию запроса никак не влияет Но это все не касается LEFT, RIGHT и FULL JOIN, потому что здесь сначала выполняется объединение по ON а потом уже идет фильтрация по WHERE