Подзапрос (subquery) — это запрос, вложенный внутрь другого SQL-запроса. Подзапросы позволяют выполнять сложные операции с данными, используя результаты одного запроса как входные данные для другого.
В WHERE/HAVING:
SELECT product_name, price
FROM products
WHERE price > (
SELECT AVG(price) FROM products
);
В FROM (inline view):
SELECT dept.name, emp_stats.avg_salary
FROM departments dept
JOIN (
SELECT department_id, AVG(salary) as avg_salary
FROM employees
GROUP BY department_id
) emp_stats ON dept.id = emp_stats.department_id;
В SELECT (скалярный подзапрос):
SELECT
name,
salary,
(SELECT AVG(salary) FROM employees) as avg_salary
FROM employees;
Скалярные (возвращают одно значение):
SELECT name
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
Строковые (возвращают одну строку):
SELECT *
FROM products
WHERE (category, price) = (
SELECT category, MAX(price)
FROM products
GROUP BY category
LIMIT 1
);
Колонные (возвращают набор значений одного столбца):
SELECT name
FROM customers
WHERE id IN (
SELECT customer_id
FROM orders
WHERE order_date > '2023-01-01'
);
Табличные (возвращают таблицу):
SELECT *
FROM (
SELECT department, COUNT(*) as emp_count
FROM employees
GROUP BY department
) dept_stats
WHERE emp_count > 10;
Независимые (самостоятельные):
SELECT name
FROM products
WHERE category_id IN (
SELECT id FROM categories WHERE is_active = true
);
Коррелированные (зависимые) - ссылаются на поля внешнего запроса:
SELECT e.name, e.salary
FROM employees e
WHERE salary > (
SELECT AVG(salary)
FROM employees
WHERE department_id = e.department_id
);
IN/NOT IN:
SELECT name
FROM employees
WHERE id IN (
SELECT manager_id
FROM departments
);
EXISTS/NOT EXISTS (часто более эффективны):
SELECT c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
AND o.total > 1000
);
Сравнения (ANY, ALL, SOME):
SELECT name, salary
FROM employees
WHERE salary > ALL (
SELECT salary
FROM employees
WHERE department = 'Support'
);
-- Вместо:
SELECT name FROM products
WHERE category_id IN (SELECT id FROM categories WHERE type = 'ELECTRONICS');
-- Лучше:
SELECT p.name
FROM products p
JOIN categories c ON p.category_id = c.id
WHERE c.type = 'ELECTRONICS';
Использование EXISTS вместо IN для больших наборов данных
Ограничение выборки во внутренних запросах:
SELECT name
FROM employees e
WHERE EXISTS (
SELECT 1
FROM sales s
WHERE s.employee_id = e.id
AND s.amount > 10000
LIMIT 1 -- Достаточно одной записи для EXISTS
);
SELECT
d.department_name,
(SELECT COUNT(*) FROM employees e WHERE e.department_id = d.department_id) as emp_count,
(SELECT MAX(salary) FROM employees e WHERE e.department_id = d.department_id) as max_salary
FROM
departments d
WHERE
(SELECT AVG(salary) FROM employees e WHERE e.department_id = d.department_id) >
(SELECT AVG(salary) FROM employees)
ORDER BY
emp_count DESC;
Подзапросы — мощный инструмент SQL, который позволяет:
Ключевые моменты:
Грамотное использование подзапросов значительно расширяет возможности работы с данными в SQL.