Подзапросы обзорно — когда запрос удобнее вложить внутрь запроса

Иногда для проверки нужен результат ОДНОГО запроса, чтобы использовать его как условие в ДРУГОМ запросе — например, «сначала найди всех, кто записан на курс, а потом среди ОСТАЛЬНЫХ найди тех, кого там нет». Для таких задач в 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)?

АФункция, которая соединяет две таблицы по внешнему ключу
БОтдельная база данных для тестовых проверок и отладки
ВSQL-запрос внутри другого запроса как условие фильтра

Подсказка: В названии уже есть подсказка — это запрос ВНУТРИ запроса.

Онлайн-проверка ответа появится позже

Контрольный вопрос. В запросе SELECT name FROM users WHERE id IN (SELECT user_id FROM enrollments WHERE course_id = 5); подзапрос простой, несвязанный — он не ссылается на внешний запрос. Какой запрос выполняется первым по логике?

АВнешний: SELECT name FROM users
БВнутренний: SELECT user_id FROM enrollments WHERE course_id = 5
ВОба выполняются одновременно и независимо друг от друга

Подсказка: Внешнему запросу нужен результат внутреннего, чтобы понять, какие id искать — значит, порядок не одновременный.

Онлайн-проверка ответа появится позже

Контрольный вопрос. Чем подзапрос обычно проще для чтения, чем эквивалентный JOIN?

АПодзапрос не требует условия WHERE вообще
БЛогика более пошаговая: «сначала найди X, потом по X найди Y»
ВПодзапрос всегда работает быстрее JOIN на любых объёмах данных

Подсказка: В уроке прямо сравнивается «пошаговое чтение» подзапроса с соединением таблиц через JOIN.

Онлайн-проверка ответа появится позже

Контрольный вопрос. Что верно про скорость подзапроса с IN и эквивалентного ему JOIN в современной СУБД?

АЧасто одинаково: СУБД сама превращает IN/EXISTS в соединение таблиц
БJOIN всегда быстрее подзапроса на любых данных
ВПодзапрос всегда быстрее, потому что выполняется один раз

Подсказка: Вспомни, что делает с таким запросом сама СУБД перед выполнением. Слово «всегда» в вариантах — повод насторожиться.

Онлайн-проверка ответа появится позже

Контрольный вопрос. Про соотношение подзапроса и JOIN в уроке сказаны следующие вещи. Какие из утверждений верны (выбери все подходящие)?

Выберите все верные варианты.

АПодзапрос и JOIN часто взаимозаменяемы для одной и той же задачи
БПодзапрос всегда выполняется быстрее JOIN, потому что обращается к таблице один раз
ВДля разовой ручной проверки тестировщика подзапрос часто удобнее читать
ГСовременные СУБД часто выполняют IN-подзапрос и эквивалентный JOIN одинаково быстро
ДПодзапрос можно вложить только на один уровень, глубже вложенность запрещена
ЕПодзапрос никогда не используется вместе с WHERE

Подсказка: Сверь каждое утверждение с разделами «Подзапрос против 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.

✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.

Онлайн-проверка ответа появится позже

Что дальше

Теперь вы умеете решать задачи подзапросом и оценивать, когда это оправдано. Дальше — новые приёмы работы с данными.

← Назад  ·  ↑ В начало урока  ·  ⌂ В начало курса  ·  Вперёд →

Школа Виктора Комлева