Подзапросы, EXISTS и IN
Классификация по позиции
| Позиция | Назначение | Требование |
|---|---|---|
SELECT | Скалярное значение в проекции | Одна строка, один столбец |
FROM | Производная таблица | Обязателен псевдоним |
WHERE | Фильтрация | IN, EXISTS, сравнение с подзапросом |
Коррелированные подзапросы
Подзапрос ссылается на столбцы внешнего запроса. Для каждой строки внешнего запроса подзапрос выполняется заново (если оптимизатор не перепишет его в JOIN).
-- Товары дороже средней цены в своей категории
SELECT name, category, price
FROM products p1
WHERE price > (
SELECT AVG(price)
FROM products p2
WHERE p2.category = p1.category
);
Скаляр в проекции:
SELECT
p.name,
p.price,
(SELECT AVG(price) FROM products p2 WHERE p2.category = p.category) AS avg_in_category,
p.price - (SELECT AVG(price) FROM products p2 WHERE p2.category = p.category) AS deviation
FROM products p
ORDER BY ABS(
p.price - (SELECT AVG(price) FROM products p2 WHERE p2.category = p.category)
) DESC;
EXISTS и NOT EXISTS
Проверяют, вернул ли подзапрос хотя бы одну строку. Содержимое SELECT не важно — обычно пишут SELECT 1.
-- Клиенты, у которых есть хотя бы один заказ
SELECT c.full_name, c.email
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
);
-- Клиенты без заказов
SELECT c.full_name
FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);
EXISTS устойчив к NULL в подзапросе: важно только наличие строк.
IN и NOT IN
SELECT name, price
FROM products
WHERE category IN ('Книги', 'Канцелярия');
SELECT product_id
FROM order_items
WHERE order_id IN (SELECT order_id FROM orders WHERE status = 'shipped');
Ловушка NOT IN и NULL
Если подзапрос в NOT IN возвращает хотя бы одно NULL в сравниваемом столбце, результат всего предиката — UNKNOWN, и ни одна строка не пройдёт фильтр.
-- Опасно, если customer_id в orders бывает NULL:
SELECT name FROM customers
WHERE customer_id NOT IN (SELECT customer_id FROM orders);
Рекомендация: для проверки отсутствия связи использовать NOT EXISTS или отфильтровать NULL в подзапросе:
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);
-- либо
WHERE customer_id NOT IN (
SELECT customer_id FROM orders WHERE customer_id IS NOT NULL
);
ALL и ANY
-- Цена выше всех цен в категории «Книги»
SELECT name, price FROM products
WHERE price > ALL (SELECT price FROM products WHERE category = 'Книги');
-- Цена выше хотя бы одной цены поставщика
SELECT name FROM products p
WHERE price > ANY (SELECT supply_price FROM product_suppliers ps WHERE ps.product_id = p.product_id);
> ANY эквивалентно > MIN(подзапрос); > ALL — > MAX(подзапрос).
Подзапрос или JOIN?
| Сценарий | Конструкция | Почему |
|---|---|---|
| Проверка существования связи | EXISTS | Ясная семантика, корректно с NULL |
| Фильтр по фиксированному списку | IN (...) | Читаемость |
Список с возможным NULL | NOT EXISTS | Корректность |
| Нужны столбцы обеих таблиц | JOIN | Одно сканирование, проекция |
| Агрегат по связанной сущности | Коррелированный подзапрос или JOIN + GROUP BY | Зависит от плана |
Оптимизатор PostgreSQL часто преобразует EXISTS в semi join, коррелированный подзапрос с агрегатом — в JOIN + группировку.
Контрольные вопросы
- Чем коррелированный подзапрос отличается от некоррелированного?
- Почему
NOT INсNULLв подзапросе даёт пустой результат? - Когда
EXISTSпредпочтительнееIN? - Может ли оптимизатор не переписать коррелированный подзапрос в
JOIN?
См. также
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Вот SQL как раз обеспечивает такую связь и это главное отличие реляционных БД - реляции (relations), что означает связи. Знакомимся с языком - ставим программы, запускаем, выполняем первые запросы. От файлового хранения к реляционной и современной мультимодельной СУБД — термины, причины появления SQL и базовая классификация систем. Домены, атрибуты, кортежи и отношения — свойства реляционных таблиц и ограничения целостности при проектировании схемы. Функциональные зависимости, нормальные формы 1НФ–3НФ, аномалии обновления и осознанная денормализация при проектировании схемы. Метаданные СУБД через information_schema и pg_catalog — запросы к структуре таблиц, ключей и индексов в PostgreSQL. Логическое и физическое резервное копирование, pg_dump, pg_restore, WAL и восстановление на точку во времени (PITR). Логический порядок выполнения SELECT, проекция, WHERE, DISTINCT, ORDER BY и правила читаемого форматирования запросов. AND, OR, NOT, приоритет операторов, NULL и UNKNOWN, IS NULL, NOT IN и IS DISTINCT FROM в PostgreSQL. MVCC, уровни блокировок таблиц, FOR UPDATE, SKIP LOCKED, взаимоблокировки и диагностика через pg_locks. Учебная схема интернет-магазина для PostgreSQL — DDL и примеры запросов по темам курса SQL. Принципы работы SQL-движка - подключение к СУБД, разбор и выполнение запроса и возврат результата клиенту.SQL - язык структурированных запросов
Первые шаги с SQL
Эволюция систем хранения данных
Реляционная модель данных
Нормализация данных
Словарь данных и системные каталоги
Резервное копирование и восстановление PostgreSQL
Оператор SELECT — синтаксис и стиль
Фильтрация и трёхзначная логика
Блокировки и конкурентный доступ в PostgreSQL
Практикум shop_data
Принципы работы SQL-движка