Подзапросы / CTEСредний
Подзапрос (Subquery)
SELECT внутри другого SQL выражения. Может быть в FROM, WHERE, HAVING или SELECT.
Синтаксис
-- В WHERE: SELECT * FROM t WHERE col IN (SELECT col FROM t2); -- В FROM: SELECT * FROM (SELECT ...) sub;
Объяснение
Подзапросы бывают:
- Некоррелированные: выполняются один раз, независимо от внешнего запроса
- Коррелированные: ссылаются на внешний запрос, выполняются для каждой строки — потенциально медленно
В большинстве случаев современные оптимизаторы могут переписать подзапрос как JOIN. Но не всегда — смотри EXPLAIN.
Пример
-- Некоррелированный (быстро — один раз) SELECT * FROM products WHERE category_id IN (SELECT id FROM categories WHERE is_active = true); -- Коррелированный (медленно — для каждой строки) SELECT name, (SELECT COUNT(*) FROM orders WHERE user_id = u.id) AS orders FROM users u;
Связанные термины
CTE (Common Table Expression)Именованный подзапрос, объявленный через WITH. Делает сложные запросы читаемыми.EXISTS / NOT EXISTSПроверяет, возвращает ли подзапрос хотя бы одну строку. Более эффективен чем IN для больших данных.INNER JOINВозвращает только строки, у которых есть совпадение в обеих таблицах.
Анекдоты по теме
— Доктор, у меня SELECT в SELECT. — Это подзапрос. Неопасно, если не вложен в WHERE. — А если в WHERE? — Тогда держитесь. А если коррелированный? — Тогда пишите завещание.
— Почему мой запрос не находит «Иванов»? — Покажи WHERE. — WHERE name = 'иванов' — Регистр. — О. — Используй ILIKE или LOWER().
— Зачем нужен EXPLAIN (BUFFERS)? — Показывает, сколько данных прочитано из кэша и с диска. — Зачем это знать? — Если всё из кэша — ок. Если много с диска — нужно больше shared_buffers или индексы.