JavaRush /Курсы /SQL SELF /Использование SELECT внутри SELECT

Использование SELECT внутри SELECT

SQL SELF
13 уровень , 1 лекция
Открыта

Представьте, что вы работаете аналитиком в университете (мы же делаем базу данных для университета, помните?). Вас попросили не просто вывести студентов и их оценки, а добавить в таблицу столбец с максимальной оценкой в группе, чтобы легко сравнивать результаты. Как решить эту задачу? Конечно, с помощью подзапросов в SELECT!

Подзапрос в SELECT позволяет вычислять значения прямо во время выполнения основного запроса. Это круто, потому что вы можете комбинировать агрегированные расчёты, сложные фильтры и даже другую коллекцию данных в одном запросе.

Основы вложенных запросов в SELECT

Подзапрос в SELECT буквально работает так, как звучит: вы вставляете результат одного SELECT внутрь другого. Это позволяет рассчитать дополнительные значения для каждой строки результата.

Вот простой пример. Допустим, у нас есть таблица students со следующей структурой:

student_id name group_id
1 Linda 101
2 Otto 102
3 Anna 101

И таблица grades:

grade_id student_id grade
1 1 5
2 1 4
3 2 3
4 3 5
5 3 4

Пример 1: Добавление максимальной оценки в группе

Задача: вывести имена студентов, их оценки и максимальную оценку в группе, чтобы было видно, насколько оценки каждого студента отличаются от лучших результатов группы.

SQL-код:

SELECT
    s.name AS student_name,
    g.grade AS student_grade,
    (
        SELECT MAX(grade) -- данный запрос возвращает одно единственное значение 
        FROM grades 
        INNER JOIN students ON grades.student_id = students.student_id
        WHERE students.group_id = s.group_id
    ) AS max_group_grade 
FROM 
    students s
INNER JOIN 
    grades g ON s.student_id = g.student_id;

Что здесь происходит:

  1. Для каждого студента мы получаем его имя и оценку (s.name, g.grade).
  2. SELECT MAX(grade) — это подзапрос, который возвращает максимальную оценку внутри группы студента.
  3. Подзапрос выполняется для каждой строки основного запроса и использует условие WHERE students.group_id = s.group_id, чтобы ограничить выборку одной группой.

Пример 2: Средняя оценка по группе

Хотите быть ещё более полезным для аналитиков? Добавим в вывод не только максимальную оценку, но и среднюю оценку по группе.

SQL-код:

SELECT
    s.name AS student_name,
    g.grade AS student_grade,
    (
        SELECT AVG(grade) 
        FROM grades 
        INNER JOIN students ON grades.student_id = students.student_id
        WHERE students.group_id = s.group_id
    ) AS avg_group_grade
FROM 
    students s
INNER JOIN 
    grades g ON s.student_id = g.student_id;

Теперь:

  • Вместо MAX() мы используем AVG() для вычисления средней оценки в группе.
  • Получаем "живой" анализ данных.

Ограничения и рекомендации

Подзапросы в SELECT мощны, но их использование требует осторожности:

  1. Производительность. Каждый подзапрос выполняется для каждой строки основного запроса. Это может замедлить выполнение SQL-запроса, если таблицы большие. Например, если в таблице 1000 студентов, то подзапрос выполнится 1000 раз!
  2. Индексы. Для ускорения выполнения таких запросов важно правильно индексировать столбцы, которые участвуют в условиях WHERE подзапроса.
  3. Читаемость. Старайтесь избегать избыточной вложенности. Если подзапросы становятся слишком сложными, подумайте о переносе их в FROM или создании временных таблиц.

Примеры использования

Давайте рассмотрим ещё несколько интересных кейсов.

Пример 3: Количество курсов у каждого студента

Выводим таблицу, где для каждого студента отображается количество курсов, на которые он записан. Таблица enrollments связана со студентами по student_id:

student_id course_id
1 201
1 202
2 201
3 203

SQL-код:

SELECT
    s.name AS student_name,
    (
        SELECT COUNT(*) 
        FROM enrollments
        WHERE enrollments.student_id = s.student_id
    ) AS course_count
FROM 
    students s;

Здесь подзапрос подсчитывает количество записей в таблице enrollments для каждого студента.

Пример 4: Флаг "отличник" для каждого студента

Покажем, является ли студент отличником. Пусть критерий для отличника — это наличие всех своих оценок на уровне 5.

SQL-код:

SELECT
    s.name AS student_name,
    (
        SELECT CASE 
            WHEN MIN(g.grade) = 5 THEN 'Отличник'
            ELSE 'Не отличник'
        END
        FROM grades g
        WHERE g.student_id = s.student_id
    ) AS status
FROM 
    students s;

Здесь используется вложенный CASE, чтобы присвоить статус "Отличник" только тем студентам, у которых все оценки равны 5.

Оптимизация подзапросов в SELECT

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

  1. Используйте индексы. Если подзапросы фильтруют данные, убедитесь, что на используемых столбцах есть индексы.
  2. Кэшируйте результаты. Иногда целесообразно вынести подзапросы в представления (VIEW) или временные таблицы.
  3. Меньше вложенности. Не злоупотребляйте многократной вложенностью, если можно использовать более простой подход.

Подзапросы в SELECT открывают массу возможностей для вычислений и анализа данных. Хотя они могут быть ресурсоёмкими, их правильно оптимизированное использование делает SQL гораздо более выразительным и гибким. Так что не стесняйтесь экспериментировать и находить свои способы улучшать запросы!

2
Задача
SQL SELF, 13 уровень, 1 лекция
Недоступна
Подсчёт количества курсов для каждого студента
Подсчёт количества курсов для каждого студента
2
Задача
SQL SELF, 13 уровень, 1 лекция
Недоступна
Вывод средней оценки студента по оценкам
Вывод средней оценки студента по оценкам
Комментарии (9)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Сергій Бабич Уровень 33
16 декабря 2025
Поправте підказки до задачі 2. Спочатку при помилках пише, що можна використати ROUND, або TO_CHAR. Коли вибрав TO_CHAR, то знову помилка і пише, що краще використати ROUND.
Сергей Третяк Уровень 14
30 октября 2025
задача №2: Основной запрос должен связывать таблицы `students` и `grades` по полю `student_id`, чтобы получить информацию о студентах и их оценках. если его использовать, то строки увеличиваются на количество оценок каждого студента, логично было бы тогда выводить, не два а три столбца "имя", "оценка", "средняя оценка" Иначе пропадает смысл в этом условии, и получаем странную таблицу
Anemon Уровень 13 Expert
20 сентября 2025
🤓
danmol_k Уровень 14
2 сентября 2025
Тут будет вообще работать что-то нормально?
Denis Murashko Уровень 24
27 августа 2025
Запрос должен соединять таблицы `students` и `enrollments` по полю `student_id` для получения информации о студентах и их записях на курсы. JOIN в задаче 1 не нужен,было бы неплохо перефразировать
Никита Иванов Уровень 25
6 июля 2025
На мой взгляд должны быть более применимые задачи, т.к. обе задачи и большинство примеров следует решать через group by
Ra Уровень 35 Student
28 июля 2025
а какой пример самый подходящий без group by?
Евгений Уровень 49 Expert
29 июля 2025
Согласен, хоть и не уверен насчёт GROUP BY. Проще тут использовать вложенные запросы, но в блоке FROM или JOIN, а не в SELECT
Vlad Tagunkov Уровень 35
27 декабря 2025
я первую задачу сразу решил с груп бай - там оно кажется более логичным.