CTE vs подзапрос: разница и что выбрать на собесе

CTE (Common Table Expression, WITH) и подзапросы решают одну задачу — выделить промежуточный результат. CTE именованы и могут использоваться несколько раз, подзапросы — вложены и одноразовы. На собесе оценивают, понимаете ли вы разницу в читаемости и плане выполнения.

Если коротко. CTE — именованный кусок логики в WITH, можно переиспользовать и рекурсировать. Подзапрос — одноразовый вложенный SELECT.
Проверь себя · 1/3разбор после ответа
Вы считаете метрику: доля пользователей, которые совершили покупку в течение 7 дней после регистрации. Какой вариант построения запроса делает логику наиболее проверяемой и удобной для отладки?

CTE vs Подзапрос: таблица различий

ПараметрCTEПодзапрос
ИменованныйДаНет
ПереиспользованиеДа, в рамках запросаНет, только инлайн
РекурсияДа (WITH RECURSIVE)Нет
ЧитаемостьВысокая (видна декомпозиция)Падает с вложенностью
План выполненияМожет матерализоваться (PG)Обычно инлайнится

Когда использовать CTE

Когда использовать Подзапрос

На примере

CTE — читаемая декомпозиция
WITH active AS (
  SELECT user_id FROM users WHERE status = 'active'
),
big AS (
  SELECT user_id FROM orders WHERE amount > 1000
)
SELECT * FROM active a JOIN big b USING (user_id);
Подзапрос — компактно
SELECT *
FROM (SELECT user_id FROM users WHERE status = 'active') AS active
JOIN (SELECT user_id FROM orders WHERE amount > 1000) AS big USING (user_id);

Ловушка на собесе

В Postgres до версии 12 CTE были «оптимизационным барьером» — оптимизатор не пушил предикаты внутрь. Стоит знать, если идёте на собес в команду с PG <12. С 12+ — поведение как у подзапросов.
Тренировать SQL в Telegram

Ещё сравнения по теме