Ответ
Подзапрос (subquery) — это SQL-запрос, вложенный внутрь другого основного запроса (SELECT, INSERT, UPDATE, DELETE). Его результат используется внешним запросом как условие, источник данных или вычисляемое значение.
Основные типы и использование:
- Скалярный подзапрос (возвращает одно значение):
SELECT name FROM employees WHERE salary > (SELECT AVG(salary) FROM employees); - Подзапрос, возвращающий множество строк (используется с
IN,ANY,ALL):SELECT name FROM departments WHERE id IN (SELECT department_id FROM projects WHERE budget > 100000); - Подзапрос в качестве таблицы (в
FROM):SELECT dept_name, avg_sal FROM (SELECT department_id, AVG(salary) as avg_sal FROM employees GROUP BY department_id) AS dept_stats WHERE avg_sal > 50000;
Коррелированные vs. Некоррелированные:
- Некоррелированный выполняется один раз независимо от внешнего запроса.
- Коррелированный выполняется для каждой строки внешнего запроса, ссылаясь на его значения (может быть менее производительным).
Производительность: Часто подзапрос можно переписать с использованием JOIN, что может быть эффективнее, особенно для коррелированных подзапросов. Выбор зависит от оптимизатора СУБД и структуры данных.
Ответ 18+ 🔞
Слушай, давай разберёмся с этими подзапросами, а то у некоторых от одного слова голова квадратная становится. Представь, что это как матрёшка, только в SQL. Берёшь один запрос и засовываешь его прямо в живот другому. Внешний запрос потом этим результатом пользуется — как условием, как данными или просто как цифрой для расчётов. Всё просто, ёпта.
Какие они бывают и куда их пихают:
-
Подзапрос-одиночка (скалярный). Возвращает одно-единственное значение, одну ячейку. Идеально для сравнений.
SELECT name FROM employees WHERE salary > (SELECT AVG(salary) FROM employees);Тут внутренний запрос (
SELECT AVG...) посчитает среднюю зарплату по больнице один раз, и внешний запрос с этим числом уже работает. Удобно, правда? -
Подзапрос-список. Возвращает целый столбик значений, несколько строк. Тут уже нужны специальные операторы вроде
IN.SELECT name FROM departments WHERE id IN (SELECT department_id FROM projects WHERE budget > 100000);Перевод на человеческий: «Дай мне названия отделов, у которых есть проекты с бюджетом больше ста тысяч». Внутренний запрос выплёвывает кучу
department_id, а внешний по этому списку отфильтровывает. -
Подзапрос как целая виртуальная таблица. Самый хардкор — когда результат подзапроса используется прямо как источник данных в
FROM. Выглядит страшновато, но мощно.SELECT dept_name, avg_sal FROM (SELECT department_id, AVG(salary) as avg_sal FROM employees GROUP BY department_id) AS dept_stats WHERE avg_sal > 50000;Сначала мы создаём временную таблицу
dept_statsсо средней зарплатой по отделам, а потом из неё уже выбираем только те, где средняя зарплата выше 50к. Ёперный театр, а не подход!
Важный момент, блядь: коррелированные vs. некоррелированные.
- Некоррелированный — как самостоятельная песенка. Выполнился один раз в начале, отдал результат и отдыхает. Как в первых двух примерах.
- Коррелированный — это уже зависимая хуйня. Он выполняется заново для КАЖДОЙ строки внешнего запроса, потому что ссылается на его поля. Представь, что для каждого сотрудника ты отдельно бегаешь в подзапрос что-то проверять. Производительность от этого может накрыться медным тазом, если данных овердохуища.
Про производительность: Часто эту всю конструкцию можно переписать на JOIN. Иногда JOIN быстрее, особенно для коррелированных подзапросов. А иногда оптимизатор базы данных сам всё по-умному преобразует. Так что, чувак, смотри по ситуации, смотри на планы выполнения и не бзди экспериментировать. Главное — чтобы работало и не тормозило как хуй в пальто.