До сих пор мы работали с одной таблицей за раз. Но в реальной базе нужные данные почти всегда разложены по НЕСКОЛЬКИМ связанным таблицам — как users, enrollments и courses из урока 9.1. Чтобы собрать их вместе в одном запросе, нужен JOIN.
Чему научишься за этот урок:
— объяснять, зачем нужен JOIN и как он использует внешние ключи;
— писать запрос с несколькими JOIN, собирающий данные из трёх таблиц;
— называть разницу между INNER JOIN и LEFT JOIN;
— выбирать нужный вид JOIN под конкретный тестовый сценарий.
Зачем нужен JOIN
JOIN — объединение данных из нескольких таблиц в одном запросе по СВЯЗИ между ними, то есть через внешний ключ (мы разбирали его в уроке 9.1). Без JOIN пришлось бы делать несколько отдельных запросов и вручную сопоставлять id между ними — JOIN делает это за один запрос.
INNER JOIN
INNER JOIN возвращает только те строки, для которых есть совпадение в ОБЕИХ таблицах. Если, например, у пользователя нет ни одной записи на курс — он просто не попадёт в результат INNER JOIN, потому что для него нет пары в таблице enrollments.
Начнём с самого простого случая — один JOIN между двумя таблицами:
SELECT enrollments.user_id, courses.title FROM enrollments JOIN courses ON enrollments.course_id = courses.id; -- один JOIN, две таблицы: enrollments и courses -- результат: для каждой записи на курс -- id пользователя и название курса
Теперь усложним пример: подключим сразу третью таблицу — users, — чтобы вместо голого user_id получить понятное имя пользователя:
SELECT users.name, courses.title FROM enrollments JOIN users ON enrollments.user_id = users.id JOIN courses ON enrollments.course_id = courses.id; -- просто JOIN без слова INNER работает как INNER JOIN (это значение по умолчанию) -- результат: только пользователи, у которых ЕСТЬ хотя бы одна запись на курс
Обратите внимание: слово JOIN само по себе — это и есть INNER JOIN, слово INNER можно не писать явно, база подразумевает его по умолчанию.
LEFT JOIN
LEFT JOIN возвращает ВСЕ строки из левой (первой указанной) таблицы, даже если для них НЕТ совпадения в правой таблице. Если совпадения нет — поля из правой таблицы в результате будут NULL (пусто).
SELECT users.name, enrollments.course_id FROM users LEFT JOIN enrollments ON users.id = enrollments.user_id; -- вернутся ВСЕ пользователи, даже те, у кого нет ни одной записи на курс -- у таких пользователей enrollments.course_id в результате будет NULL
Практическая разница: когда какой JOIN нужен
Разница между видами JOIN — это не теория ради теории, а вопрос ПРАВИЛЬНОГО результата под конкретную задачу тестировщика:
Сценарий А: 'Показать всех пользователей, у которых ЕСТЬ хотя бы одна запись на курс' -> нужен INNER JOIN (или просто JOIN) Сценарий Б: 'Найти пользователей БЕЗ единой записи на курс' (например, для рассылки 'вы давно не заходили') -> нужен LEFT JOIN + WHERE enrollments.id IS NULL (строки, где справа не нашлось совпадения -- как раз те, у кого нет записей)
SELECT users.name FROM users LEFT JOIN enrollments ON users.id = enrollments.user_id WHERE enrollments.id IS NULL; -- вернёт только тех пользователей, для кого справа не нашлось ни одной записи
Если бы для сценария Б по ошибке использовали INNER JOIN, запрос вернул бы пустой (или неполный) результат — пользователи без записей просто не попали бы в него ни при каких условиях WHERE, потому что INNER JOIN отбрасывает их ещё до фильтрации.
Итог:
— JOIN объединяет данные из нескольких таблиц по связи через внешний ключ;
— INNER JOIN (или просто JOIN) — только строки с совпадением в обеих таблицах;
— LEFT JOIN — все строки из левой таблицы, даже без совпадения справа (тогда поля справа — NULL);
— поиск ‘у кого чего-то нет’ требует LEFT JOIN + условие IS NULL на поле правой таблицы;
— выбор неверного вида JOIN даёт неполный или пустой результат, даже если сам запрос не содержит синтаксической ошибки.
Контрольный вопрос. Зачем в SQL нужен JOIN?
Подсказка: В начале урока прямо дано определение JOIN.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Что вернёт INNER JOIN, если у пользователя нет ни одной записи на курс?
Подсказка: В уроке прямо сказано, что INNER JOIN требует совпадения в ОБЕИХ таблицах.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Что делает LEFT JOIN, если для строки левой таблицы нет совпадения в правой?
Подсказка: Ключевая разница LEFT JOIN с INNER JOIN — что происходит именно в этом случае.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Что означает просто ключевое слово JOIN без уточнения INNER или LEFT?
Подсказка: В уроке это отмечено отдельным замечанием сразу после первого примера с JOIN.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Дан запрос: SELECT users.name FROM users LEFT JOIN enrollments ON users.id = enrollments.user_id WHERE enrollments.id IS NULL; Какие из утверждений про него верны (выбери все подходящие)?
Выберите все верные варианты.
Подсказка: Одно утверждение прямо противоречит разнице между INNER JOIN и LEFT JOIN, разобранной в уроке.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Контрольный вопрос. Какой вид JOIN нужен, чтобы вернуть ВСЕ строки из первой (левой) таблицы, даже без совпадения во второй? Ответь двумя словами по-английски, как в уроке.
Подсказка: Название этого вида JOIN отражает, из какой таблицы берутся все строки.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Задание. На основе какой связи между таблицами работает JOIN — через какой вид ключа (из уже пройденных в курсе)? Ответь двумя словами по-русски.
Подсказка: Это ключ из урока 9.1, который ссылается на первичный ключ другой таблицы.
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Задание. Тестировщик выполнил запрос с INNER JOIN между users и enrollments и получил 40 строк, хотя в таблице users всего 50 пользователей. Сколько пользователей из 50 не попали в результат из-за отсутствия записи на курс? Ответь числом.
Подсказка: Вспомни, кого INNER JOIN отбрасывает из результата.
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Проверить ответ →
Реальная проверка — на нашей платформе, бесплатно и без регистрации. Открыть задачу и ввести ответ →
Задание. Даны два тестовых сценария:
Сценарий 1: нужен список пользователей ВМЕСТЕ с названиями курсов, на которые они записаны, но интересуют ТОЛЬКО те, у кого есть хотя бы одна запись.
Сценарий 2: нужен список ВСЕХ пользователей, включая тех, кто ещё ни на один курс не записался, чтобы отправить им письмо с предложением записаться.
Для каждого сценария укажи, какой вид JOIN нужен (INNER JOIN или LEFT JOIN), и обоснуй выбор в 1-2 предложениях на сценарий.Критерий приёмки: для сценария 1 назван INNER JOIN с обоснованием, что нужны только пользователи с совпадением в обеих таблицах; для сценария 2 назван LEFT JOIN с обоснованием, что нужны ВСЕ пользователи, включая тех, у кого нет совпадения (записи) в enrollments.
Подсказка: Ключевое слово в каждом сценарии — ‘только те, у кого есть’ против ‘все, включая тех, у кого нет’.
✅ Готово, если: ответ раскрывает то, что просит критерий приёмки в задании выше.
Онлайн-проверка ответа появится позже
Задание. Тестировщик по ошибке использовал INNER JOIN вместо LEFT JOIN в запросе, который должен был найти пользователей БЕЗ единой записи на курс (для сценария ‘давно не заходили’). Объясни в 2-3 предложениях: (а) что вернёт такой ошибочный запрос — пустой результат или что-то другое, и почему; (б) как правильно переписать запрос, используя LEFT JOIN и условие на NULL.
Критерий приёмки: (а) объяснено, что запрос с INNER JOIN вернёт ПУСТОЙ результат (или в принципе не сможет найти нужных пользователей), потому что INNER JOIN уже на этапе объединения отбрасывает пользователей без совпадения в enrollments, и добавленное к нему условие IS NULL никогда не сработает, так как таких строк в результате INNER JOIN просто не существует; (б) назван правильный вариант — LEFT JOIN users с enrollments по user_id/id и WHERE enrollments.id IS NULL (аналогично примеру сценария Б в уроке).
Подсказка: Подумай, в какой момент INNER JOIN отбрасывает нужные строки — до или после условия WHERE IS NULL, и может ли это условие вообще на что-то сработать после INNER JOIN.
✅ Готово, если: ответ раскрывает то, что просит критерий приёмки в задании выше.
Онлайн-проверка ответа появится позже
Что дальше
Теперь вы умеете собирать данные из нескольких связанных таблиц и выбирать правильный вид JOIN под сценарий. В следующем уроке разберём GROUP BY и агрегатные функции — как считать и группировать данные, а не просто выводить их построчно.
