Начиная с PostgreSQL 12 оптимизатор получил право разворачивать
CTE прямо внутрь основного запроса.WITH data AS (
SELECT *
FROM orders
)
SELECT *
FROM data
WHERE user_id = 42;
Такой
WITH может вообще исчезнуть из плана выполнения, потому что PostgreSQL встроит его обратно в запрос. Но иногда это плохо.Например, если внутри
CTE дорогой расчёт, который нельзя выполнять повторно. Тут появляется малоизвестная фича:WITH expensive AS MATERIALIZED (
SELECT *
FROM huge_events
WHERE created_at >= now() - interval '1 day'
)
SELECT COUNT(*)
FROM expensive;
MATERIALIZED заставляет PostgreSQL сначала физически вычислить CTE, а потом использовать результат дальше.А обратная фича:
WITH data AS NOT MATERIALIZED (
SELECT *
FROM orders
)
SELECT *
FROM data
WHERE user_id = 42;
наоборот подсказывает оптимизатору агрессивно встраивать
CTE обратно в запрос.🔥 Одна и та же
CTE с MATERIALIZED и без него иногда отличается по производительности в десятки раз на больших объёмах данных.➡️ SQL Ready | #совет
