Подзапросы с IN, EXISTS, ANY, ALL
🕵️♂️ Что такое подзапросы и зачем они нужны?
Подзапросы — это SQL-запросы, вложенные внутрь других запросов. Представьте себе матрёшку: внешний запрос — это большая кукла, а внутри неё спрятаны маленькие. 🪆
Основные сценарии использования:
- Фильтрация данных (в WHERE)
- Создание вычисляемых столбцов (в SELECT)
- Формирование временных таблиц (в FROM)
-- Пример простого подзапроса
SELECT name
FROM employees
WHERE department_id IN (SELECT id FROM departments WHERE location = 'New York');
🔍 IN: Проверка принадлежности множеству
Оператор IN проверяет, совпадает ли значение с хотя бы одним элементом из списка или результата подзапроса. Это как спросить: «А этот товар есть в моём списке покупок?» 🛒
📌 Пример 1: Простое использование
SELECT product_name
FROM products
WHERE category_id IN (1, 3, 7); -- Только категории 1, 3 или 7
📌 Пример 2: С подзапросом
SELECT employee_name
FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE budget > 1000000 -- Только сотрудники из «богатых» отделов
);
💡 Лайфхак: NOT IN работает аналогично, но проверяет отсутствие значения в списке.
🎯 EXISTS: Проверка существования строк
EXISTS возвращает TRUE, если подзапрос вернул хотя бы одну строку. Это как звонок другу: «Ты ещё в офисе?» — если ответ «да», значит можно заходить. ☎️
📌 Пример 1: Базовый сценарий
SELECT supplier_name
FROM suppliers
WHERE EXISTS (
SELECT 1
FROM products
WHERE products.supplier_id = suppliers.supplier_id
AND price < 10 -- Поставщики, у которых есть товары дешевле 10
);
📌 Пример 2: С корреляцией
SELECT department_name
FROM departments d
WHERE EXISTS (
SELECT 1
FROM employees e
WHERE e.department_id = d.department_id
AND salary > 100000 -- Отделы, где есть хотя бы один «топовый» сотрудник
);
🔥 Важно: В отличие от IN, EXISTS обычно работает быстрее на больших таблицах, так как прекращает проверку при первом совпадении.
🏆 ANY/SOME и ALL: Сравнение с набором значений
Эти операторы позволяют сравнивать значение с каждым элементом подзапроса. Разберём на примерах!
🌈 ANY/SOME: «Хотя бы один»
То же самое, что IN, но с возможностью использования операторов сравнения.
SELECT product_name
FROM products
WHERE price > ANY (
SELECT price
FROM products
WHERE category = 'Premium' -- Товары дороже хотя бы одного премиум-товара
);
🏋️♂️ ALL: «Все без исключения»
SELECT employee_name
FROM employees
WHERE salary > ALL (
SELECT salary
FROM employees
WHERE department = 'Support' -- Зарплата выше ВСЕХ сотрудников поддержки
);
💎 Золотое правило:
= ANYэквивалентенIN<> ALLэквивалентенNOT IN
🧩 Комбинируем подходы: Продвинутые примеры
📌 Пример 1: EXISTS + агрегация
SELECT customer_id
FROM orders o
WHERE EXISTS (
SELECT 1
FROM order_details od
WHERE od.order_id = o.order_id
GROUP BY od.order_id
HAVING SUM(od.quantity) > 10 -- Заказы с общим количеством товаров > 10
);
📌 Пример 2: ALL с подзапросом
SELECT name
FROM students
WHERE gpa > ALL (
SELECT gpa
FROM students
WHERE graduation_year = 2020 -- Студенты с GPA выше всех выпускников 2020 года
);
🚀 Оптимизация подзапросов: ТОП-3 совета
- Индексы — убедитесь, что поля в условиях соединения проиндексированы
- EXISTS vs IN — для проверки существования
EXISTSобычно эффективнее - LIMIT в подзапросах — если нужно проверить только наличие, добавьте
LIMIT 1
🏁 Закрепляем знания
Попробуйте решить:
- Найти клиентов, которые покупали ВСЕ товары из категории «Электроника»
- Вывести продукты, которые дороже СРЕДНЕЙ цены в их категории
- Найти отделы, где НЕТ сотрудников младше 30 лет
-- Подсказка для первого задания:
SELECT customer_id
FROM customer_products cp
WHERE NOT EXISTS (
SELECT 1
FROM products p
WHERE p.category = 'Electronics'
AND NOT EXISTS (
SELECT 1
FROM purchases pu
WHERE pu.product_id = p.product_id
AND pu.customer_id = cp.customer_id
)
);