Подзапросы: вложенные запросы в WHERE, FROM, SELECT
Что такое подзапросы и зачем они нужны? 🔎
Подзапросы (или вложенные запросы) — это SQL-запросы внутри других запросов. Они позволяют выполнять сложные выборки данных, используя результаты одного запроса в другом.
Представьте себе матрешку 🪆 — подзапросы работают по такому же принципу! Их можно использовать в:
- Условиях
WHEREдля фильтрации данных - Части
FROMдля создания временных таблиц - Части
SELECTдля вычисления значений
Подзапросы делают ваш код мощнее, гибче и читабельнее. Давайте разберем каждый вариант на практике!
Подзапросы в WHERE: фильтрация по результату другого запроса 🎯
Самый распространенный случай — использование подзапроса в условии WHERE. Это позволяет фильтровать данные на основе другого запроса.
-- Найдем сотрудников, чья зарплата выше средней
SELECT employee_name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
Как это работает:
- Внутренний запрос
(SELECT AVG(salary) FROM employees)вычисляет среднюю зарплату - Внешний запрос выбирает только тех сотрудников, чья зарплата больше этого значения
Важно: При использовании с операторами сравнения (>, <, =) подзапрос должен возвращать ОДНО значение.
Подзапросы в FROM: работа с временными таблицами 📊
Когда подзапрос находится в части FROM, он создает временную таблицу, с которой можно работать как с обычной.
-- Найдем отделы с количеством сотрудников больше 5
SELECT d.department_name, emp_count.count
FROM departments d
JOIN (
SELECT department_id, COUNT(*) as count
FROM employees
GROUP BY department_id
) emp_count ON d.department_id = emp_count.department_id
WHERE emp_count.count > 5;
Разберем по шагам:
- Внутренний запрос создает временную таблицу
emp_countс количеством сотрудников по отделам - Основной запрос соединяет эту таблицу с таблицей отделов
- Фильтрует только те отделы, где сотрудников больше 5
Совет: Всегда используйте алиасы для подзапросов в FROM!
Подзапросы в SELECT: вычисления на лету 🧮
Подзапросы в SELECT позволяют добавлять вычисляемые столбцы на основе других данных.
-- Для каждого сотрудника выведем его зарплату и разницу со средней
SELECT
employee_name,
salary,
salary - (SELECT AVG(salary) FROM employees) AS diff_from_avg
FROM employees;
Особенности:
- Подзапрос должен возвращать ровно одно значение
- Может использоваться в выражениях (как в примере выше)
- Полезен для сравнительного анализа
Коррелированные подзапросы: связь с внешним запросом 🔗
Особый вид подзапросов — коррелированные, где внутренний запрос ссылается на данные внешнего.
-- Найдем сотрудников с зарплатой выше средней в их отделе
SELECT e1.employee_name, e1.salary, e1.department_id
FROM employees e1
WHERE e1.salary > (
SELECT AVG(e2.salary)
FROM employees e2
WHERE e2.department_id = e1.department_id
);
Что происходит:
- Для каждой строки внешнего запроса подзапрос вычисляет среднюю зарплату ТОЛЬКО в текущем отделе
- Сравнивает зарплату сотрудника с этим значением
Важно: Такие запросы могут быть медленными на больших таблицах!
EXISTS и NOT EXISTS: проверка на существование 🔍
Эти операторы работают с подзапросами и проверяют, возвращает ли подзапрос хотя бы одну строку.
-- Найдем клиентов, которые делали заказы в этом году
SELECT customer_name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
AND YEAR(order_date) = YEAR(CURRENT_DATE)
);
Почему SELECT 1?
Для EXISTS важно только наличие строк, поэтому выбираем константу для оптимизации.
Лучшие практики работы с подзапросами 🏆
- Оптимизация: Подзапросы в
WHEREмогут быть медленными — иногда лучше использовать JOIN - Читаемость: Сложные подзапросы лучше выносить в CTE (WITH-запросы)
- Тестирование: Всегда проверяйте подзапрос отдельно перед вставкой
- Индексы: Убедитесь, что поля для соединения проиндексированы
-- Пример оптимизации через JOIN вместо подзапроса
SELECT e.employee_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE d.location = 'New York';
Подведем итоги 🎓
- WHERE: Фильтрация по результату подзапроса (часто с операторами сравнения)
- FROM: Создание временных таблиц для последующих соединений
- SELECT: Добавление вычисляемых столбцов на основе других данных
- EXISTS/NOT EXISTS: Проверка на существование записей
- Коррелированные подзапросы: Связь с внешним запросом для контекстной выборки
Теперь вы готовы создавать сложные запросы любой глубины! Пробуйте разные подходы и выбирайте оптимальный для каждой задачи.