Вложенный запрос (subquery) — это запрос SQL, который находится внутри другого запроса. Внутренний запрос выполняется первым, а его результат используется внешним запросом.
Вложенные запросы позволяют решать сложные задачи поэтапно, разбивая их на более простые части.
Синтаксис
SELECT столбцы
FROM таблица
WHERE столбец оператор (SELECT столбец FROM таблица WHERE условие);
Типы вложенных запросов
1. Скалярный подзапрос (возвращает одно значение)
Возвращает ровно одно значение (одна строка, один столбец).
Пример:
-- Найти сотрудников с зарплатой выше средней
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
Как это работает:
- Сначала выполняется
SELECT AVG(salary) FROM employees → получаем, например, 50000
- Затем выполняется внешний запрос:
WHERE salary > 50000
2. Многострочный подзапрос (возвращает несколько значений)
Возвращает несколько строк (один столбец). Используется с операторами IN, ANY, ALL, EXISTS.
Оператор IN
-- Найти сотрудников, работающих в отделах продаж или маркетинга
SELECT name, department_id
FROM employees
WHERE department_id IN (
SELECT id
FROM departments
WHERE department_name IN ('Sales', 'Marketing')
);
Оператор EXISTS
Проверяет, есть ли хотя бы одна строка в результате подзапроса.
-- Найти отделы, в которых есть сотрудники
SELECT department_name
FROM departments d
WHERE EXISTS (
SELECT 1
FROM employees e
WHERE e.department_id = d.id
);
Особенность EXISTS: возвращает TRUE или FALSE, не возвращает данные. Поэтому часто пишут SELECT 1 для экономии ресурсов.
Оператор NOT EXISTS
-- Найти отделы без сотрудников
SELECT department_name
FROM departments d
WHERE NOT EXISTS (
SELECT 1
FROM employees e
WHERE e.department_id = d.id
);
Операторы ANY и ALL
ANY — условие истинно, если выполняется хотя бы для одного значения:
-- Сотрудники с зарплатой больше, чем у любого сотрудника отдела 5
SELECT name, salary
FROM employees
WHERE salary > ANY (
SELECT salary
FROM employees
WHERE department_id = 5
);
ALL — условие истинно, если выполняется для всех значений:
-- Сотрудники с зарплатой больше, чем у всех сотрудников отдела 5
SELECT name, salary
FROM employees
WHERE salary > ALL (
SELECT salary
FROM employees
WHERE department_id = 5
);
3. Коррелированный подзапрос
Коррелированный подзапрос — это подзапрос, который ссылается на столбцы из внешнего запроса. Он выполняется заново для каждой строки внешнего запроса.
-- Сотрудники с зарплатой выше средней в их отделе
SELECT name, department_id, salary
FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e2.department_id = e1.department_id
);
Как это работает:
- Для каждого сотрудника (e1) внешнего запроса
- Выполняется подзапрос, который вычисляет среднюю зарплату в его отделе
- Сравнивается зарплата сотрудника со средней
Важно: Коррелированные подзапросы могут быть медленными на больших таблицах, так как выполняются многократно.
4. Подзапрос в FROM (производная таблица)
Подзапрос может использоваться вместо таблицы в секции FROM.
-- Средняя зарплата по отделам, затем найти отделы с зарплатой выше общей средней
SELECT department_id, avg_salary
FROM (
SELECT department_id, AVG(salary) as avg_salary
FROM employees
GROUP BY department_id
) AS dept_avg
WHERE avg_salary > (SELECT AVG(salary) FROM employees);
Обязательно: В SQLite (и большинстве СУБД) производной таблице нужно дать псевдоним (alias).
5. Подзапрос в SELECT
Подзапрос может быть в списке выбираемых столбцов (должен возвращать одно значение).
-- Вывести сотрудников и среднюю зарплату по их отделу
SELECT
name,
salary,
(SELECT AVG(salary)
FROM employees e2
WHERE e2.department_id = e1.department_id
) as avg_dept_salary
FROM employees e1;