← Назад к вопросам

Что такое подзапрос в SQL?

1.0 Junior🔥 191 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Подзапрос в SQL: определение и суть

Подзапрос (англ. subquery или nested query) — это запрос SELECT, вложенный внутрь другого SQL-запроса (основного запроса). Он выполняется первым, а его результат используется внешним запросом для дальнейшей обработки. Фактически, это способ выполнить многоэтапную операцию за один шаг, используя результат одного запроса как данные или условие для другого.

Основная идея: подзапрос возвращает набор данных (одну строку, один столбец, таблицу или скалярное значение), который "подставляется" в основной запрос. Это мощный инструмент для сложной фильтрации, вычислений и манипуляций с данными, когда невозможно или неэффективно получить ответ одним простым SELECT.

Ключевые характеристики подзапросов

  • Вложенность: Подзапрос всегда находится внутри круглых скобок ().
  • Порядок выполнения: Как правило, СУБД сначала выполняет самый внутренний подзапрос.
  • "Независимость": Подзапрос может выполняться самостоятельно (если его извлечь из основного запроса).
  • Типы результатов: Результат подзапроса может быть:
    *   **Скалярным** (одно значение, одна строка и один столбец).
    *   **Векторным** (один столбец с несколькими строками).
    *   **Табличным** (несколько строк и столбцов).

Основные типы и примеры подзапросов

1. Подзапрос в условии WHERE (для фильтрации)

Самый частый случай. Подзапрос определяет набор значений для операторов IN, NOT IN, EXISTS, сравнений (=, >, < и др.).

Пример с IN: Найти всех сотрудников, которые работают в отделах, расположенных в Лондоне.

SELECT employee_id, first_name, last_name, department_id
FROM employees
WHERE department_id IN (
    SELECT department_id
    FROM departments
    WHERE location_id IN (
        SELECT location_id
        FROM locations
        WHERE city = 'London'
    )
);

Здесь два вложенных подзапроса последовательно определяют, какие department_id относятся к Лондону.

Пример с EXISTS: Найти менеджеров, у которых есть подчиненные.

SELECT manager_id, first_name, last_name
FROM employees e1
WHERE EXISTS (
    SELECT 1
    FROM employees e2
    WHERE e2.manager_id = e1.employee_id
);

Подзапрос возвращает логическое значение TRUE/FALSE. SELECT 1 — распространенная практика, так как EXISTS проверяет наличие строк, а не их содержимое.

2. Подзапрос в SELECT (скалярный подзапрос)

Используется для вычисления значения в каждой строке результата.

Пример: Получить список сотрудников с указанием средней зарплаты по компании в каждой строке.

SELECT
    employee_id,
    first_name,
    salary,
    (SELECT AVG(salary) FROM employees) AS avg_company_salary,
    salary - (SELECT AVG(salary) FROM employees) AS diff_from_avg
FROM employees;

Скалярный подзапрос (SELECT AVG(salary) FROM employees) выполняется один раз, и его результат подставляется в каждую строку.

3. Подзапрос в FROM (подзапрос как производная таблица)

Результат подзапроса используется как временная таблица в основном запросе, к которой можно применять JOIN, агрегацию и т.д. Обязательно требует алиаса (псевдонима).

Пример: Найти среднюю зарплату по отделам и сравнить ее с общей средней.

SELECT
    dept_avg.department_id,
    dept_avg.avg_salary,
    (SELECT AVG(salary) FROM employees) AS company_avg_salary
FROM (
    SELECT
        department_id,
        AVG(salary) AS avg_salary
    FROM employees
    WHERE department_id IS NOT NULL
    GROUP BY department_id
) AS dept_avg; -- Алиас обязателен

Здесь подзапрос в FROM создает временную таблицу dept_avg со средними зарплатами по отделам.

4. Подзапрос в операторах DML (INSERT, UPDATE, DELETE)

Позволяет использовать результат выборки для модификации данных.

Пример UPDATE: Увеличить зарплату всем сотрудникам до средней по компании.

UPDATE employees
SET salary = salary * 1.1
WHERE salary < (SELECT AVG(salary) FROM employees);

Коррелированные vs Некоррелированные подзапросы

  • Некоррелированный подзапрос: Выполняется один раз независимо от внешнего запроса. Все примеры выше (кроме EXISTS) — некоррелированные. Они самодостаточны.
  • Коррелированный подзапрос: Выполняется многократно — по одному разу для каждой строки-кандидата внешнего запроса. Его выполнение зависит от данных текущей строки внешнего запроса (происходит "корреляция").

Пример коррелированного подзапроса: Найти сотрудников, чья зарплата выше средней по их отделу.

SELECT
    e1.department_id,
    e1.employee_id,
    e1.first_name,
    e1.salary
FROM employees e1
WHERE salary > (
    SELECT AVG(salary)
    FROM employees e2
    WHERE e2.department_id = e1.department_id -- Ссылка на поле внешнего запроса!
);

Для каждого сотрудника e1 СУБД вычисляет среднюю зарплату (AVG) только по его отделу (e2.department_id = e1.department_id). Такой подзапрос может быть менее производительным на больших данных.

Важные замечания и лучшие практики

  • Производительность: Коррелированные подзапросы часто можно и стоит переписать с использованием JOIN, что обычно эффективнее для оптимизатора СУБД. Сравнение с EXISTS vs IN также зависит от данных и индексов.
  • Ограничения: Подзапрос в SELECT (скалярный) должен возвращать не более одного значения, иначе будет ошибка.
  • Читаемость: Слишком сложные вложения (>3-4 уровня) сильно ухудшают читаемость кода. В таких случаях иногда лучше использовать CTE (Common Table Expressions, WITH clause).
  • Алиасы: Всегда используйте понятные алиасы для таблиц в подзапросах, особенно в коррелированных, чтобы избежать путаницы.

Таким образом, подзапросы — это фундаментальный и гибкий механизм SQL, позволяющий строить сложные иерархические запросы. Их грамотное применение требует понимания логики выполнения, влияния на производительность и умения выбирать между подзапросом, JOIN и CTE в зависимости от конкретной задачи.