Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Подзапрос в 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, что обычно эффективнее для оптимизатора СУБД. Сравнение с
EXISTSvsINтакже зависит от данных и индексов. - Ограничения: Подзапрос в
SELECT(скалярный) должен возвращать не более одного значения, иначе будет ошибка. - Читаемость: Слишком сложные вложения (>3-4 уровня) сильно ухудшают читаемость кода. В таких случаях иногда лучше использовать CTE (Common Table Expressions,
WITHclause). - Алиасы: Всегда используйте понятные алиасы для таблиц в подзапросах, особенно в коррелированных, чтобы избежать путаницы.
Таким образом, подзапросы — это фундаментальный и гибкий механизм SQL, позволяющий строить сложные иерархические запросы. Их грамотное применение требует понимания логики выполнения, влияния на производительность и умения выбирать между подзапросом, JOIN и CTE в зависимости от конкретной задачи.