Что такое подзапрос?sql-9

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

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

1. По месту использования

В 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;

2. По типу возвращаемых данных

Скалярные (возвращают одно значение):

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'
);

Оптимизация подзапросов

  1. Преобразование в JOIN (где возможно):
-- Вместо:
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';
  1. Использование EXISTS вместо IN для больших наборов данных

  2. Ограничение выборки во внутренних запросах:

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, который позволяет:

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

Ключевые моменты:

  • Коррелированные подзапросы могут быть медленными
  • Всегда анализируйте план выполнения (EXPLAIN)
  • Рассматривайте альтернативы (JOIN, CTE)
  • Используйте подходящие операторы (IN, EXISTS, ANY)

Грамотное использование подзапросов значительно расширяет возможности работы с данными в SQL.