Статья Автор: Деникина Н.В., Деникин А.В.

Вложенный запрос

Вложенный запрос (subquery) — это запрос SQL, который находится внутри другого запроса. Внутренний запрос выполняется первым, а его результат используется внешним запросом.

Вложенные запросы позволяют решать сложные задачи поэтапно, разбивая их на более простые части.

Синтаксис

SELECT столбцы
FROM таблица
WHERE столбец оператор (SELECT столбец FROM таблица WHERE условие);

Типы вложенных запросов

1. Скалярный подзапрос (возвращает одно значение)

Возвращает ровно одно значение (одна строка, один столбец).

Пример:

-- Найти сотрудников с зарплатой выше средней
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

Как это работает:

  1. Сначала выполняется SELECT AVG(salary) FROM employees → получаем, например, 50000
  2. Затем выполняется внешний запрос: 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
);

Как это работает:

  1. Для каждого сотрудника (e1) внешнего запроса
  2. Выполняется подзапрос, который вычисляет среднюю зарплату в его отделе
  3. Сравнивается зарплата сотрудника со средней

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

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;

 

Печать