Иногда для проверки нужен результат ОДНОГО запроса, чтобы использовать его как условие в ДРУГОМ запросе — например, «сначала найди всех, кто записан на курс, а потом среди ОСТАЛЬНЫХ найди тех, кого там нет». Для таких задач в SQL есть подзапросы.
Чему научишься за этот урок:
— объяснять, что такое подзапрос (subquery) и как он устроен;
— читать запрос, где один SELECT вложен внутрь другого;
— сравнивать подзапрос с JOIN и понимать, когда они взаимозаменяемы;
— оценивать, когда подзапрос оправдан для разовой ручной проверки тестировщика.
Что такое подзапрос
Подзапрос (subquery) — это SQL-запрос ВНУТРИ другого запроса. Он используется как условие фильтрации или как источник данных для внешнего запроса. Подзапрос обычно заключён в скобки и стоит внутри WHERE, IN или другой части внешнего запроса.
Пример на знакомых таблицах users(id, name) и enrollments(id, user_id, course_id):
SELECT name FROM users WHERE id IN ( SELECT user_id FROM enrollments WHERE course_id = 5 );
Это простой, несвязанный подзапрос (внутренний запрос не ссылается на внешний), и читать его удобно изнутри наружу: внутренний запрос SELECT user_id FROM enrollments WHERE course_id = 5 находит все user_id, записанные на курс 5. Внешний запрос берёт этот список id и по нему находит в таблице users соответствующие имена. Результат — имена всех, кто записан на курс 5.
Подзапрос против JOIN
Тот же самый результат можно получить через JOIN. Подзапрос и JOIN часто взаимозаменяемы для одной и той же задачи, но подзапрос иногда проще ЧИТАТЬ: логика более пошаговая — «сначала найди X, потом по X найди Y», а не «соедини сразу две таблицы и отфильтруй».
Когда подзапрос оправдан
Для разовой, читаемой проверки тестировщику подзапрос часто удобнее JOIN — не нужно продумывать, как именно соединять таблицы, логика читается «изнутри наружу». А по скорости разницы часто нет: современные СУБД сами превращают IN и EXISTS в соединение таблиц. Поэтому для запроса, который будет выполняться часто и на больших данных (например, в автотесте), выбирают вариант, который проще читать и поддерживать, а при сомнении смотрят план выполнения — EXPLAIN (команда, которая показывает, как база собирается выполнять запрос), а не полагаются на правило «JOIN всегда быстрее».
Итог:
— подзапрос (subquery) — это SQL-запрос внутри другого запроса, используется как условие или источник данных;
— простой, несвязанный подзапрос читается так: сначала внутренний запрос, затем внешний использует его результат;
— подзапрос и JOIN часто дают один и тот же результат для одной задачи;
— по скорости они часто одинаковы: современные СУБД сами превращают IN/EXISTS в соединение, при сомнении смотрят EXPLAIN;
— для разовой ручной проверки тестировщику подзапрос часто удобнее читать.
Контрольный вопрос. Что такое подзапрос (subquery)?
Подсказка: В названии уже есть подсказка — это запрос ВНУТРИ запроса.
Онлайн-проверка ответа появится позже
Контрольный вопрос. В запросе SELECT name FROM users WHERE id IN (SELECT user_id FROM enrollments WHERE course_id = 5); подзапрос простой, несвязанный — он не ссылается на внешний запрос. Какой запрос выполняется первым по логике?
Подсказка: Внешнему запросу нужен результат внутреннего, чтобы понять, какие id искать — значит, порядок не одновременный.
Онлайн-проверка ответа появится позже
Контрольный вопрос. Чем подзапрос обычно проще для чтения, чем эквивалентный JOIN?
Подсказка: В уроке прямо сравнивается «пошаговое чтение» подзапроса с соединением таблиц через JOIN.
Онлайн-проверка ответа появится позже
Контрольный вопрос. Что верно про скорость подзапроса с IN и эквивалентного ему JOIN в современной СУБД?
Подсказка: Вспомни, что делает с таким запросом сама СУБД перед выполнением. Слово «всегда» в вариантах — повод насторожиться.
Онлайн-проверка ответа появится позже
Контрольный вопрос. Про соотношение подзапроса и JOIN в уроке сказаны следующие вещи. Какие из утверждений верны (выбери все подходящие)?
Выберите все верные варианты.
Подсказка: Сверь каждое утверждение с разделами «Подзапрос против JOIN» и «Когда подзапрос оправдан»: слова «всегда», «никогда» и «только» — повод проверить внимательнее.
Онлайн-проверка ответа появится позже
Контрольный вопрос. Какое ключевое слово SQL используется в примере урока, чтобы проверить, входит ли id пользователя в список результатов подзапроса? Ответь одним словом заглавными буквами.
Подсказка: Посмотри на пример запроса в уроке — оно стоит сразу перед открывающей скобкой подзапроса.
Онлайн-проверка ответа появится позже
Задание. Дан запрос: SELECT name FROM users WHERE id IN (SELECT user_id FROM enrollments WHERE course_id = 7); Что вернёт этот запрос? Ответь одним предложением: какой столбец и для каких пользователей.
Подсказка: Внутренний запрос находит id всех, кто записан на курс 7 — а внешний по этим id находит что?
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
Задание. Нужно найти всех пользователей, которые ЗАПИСАНЫ хотя бы на один курс (используя таблицы users(id, name) и enrollments(id, user_id, course_id)). Напиши SQL-запрос с подзапросом через IN, который вернёт их имена.
Подсказка: Подзапрос должен найти все user_id из enrollments без фильтра по course_id — а внешний запрос по ним найти имена в users.
✅ Готово, если: ты ввёл(а) верный ответ, и онлайн-проверка его приняла.
Онлайн-проверка ответа появится позже
Задание. Нужно найти всех пользователей, которые НЕ записаны ни на один курс. Опиши на словах (не обязательно точным синтаксисом SQL), как решить эту задачу через подзапрос: что должен находить внутренний запрос и как внешний запрос должен использовать его результат, чтобы получить именно «не записанных».
Подсказка: Задача — противоположность примеру из урока: там искали ЗАПИСАННЫХ через IN, здесь нужны те, кого в списке подзапроса НЕТ.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
Задание. Тестировщик получил от разработчика два варианта одного и того же проверочного запроса: (1) с подзапросом через IN — поиск пользователей, записанных на курс 5; (2) эквивалентный запрос через JOIN между users и enrollments с условием course_id = 5. Оба дают одинаковый результат. Какой вариант ты выберешь для РАЗОВОЙ ручной проверки и почему? И что учесть, если этот же запрос встроят в автотест, который выполняется сотни раз в день на базе с миллионами строк: какой вариант выбрать и как убедиться, что он не медленный? Обоснуй оба ответа.
Подсказка: Вернись к разделу «Когда подзапрос оправдан»: что там сказано про читаемость и про то, как СУБД на самом деле выполняет IN и JOIN.
✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.
Онлайн-проверка ответа появится позже
Что дальше
Теперь вы умеете решать задачи подзапросом и оценивать, когда это оправдано. Дальше — новые приёмы работы с данными.
