SQL CTE Performance

Whether a CTE is a genuine optimization barrier depends on your Postgres version.

What & Why

Before Postgres 12, every CTE was an optimization fence — the planner always computed it fully in isolation, unable to push filters from the outer query into it. Since Postgres 12, a non-recursive CTE referenced only once is inlined by default — treated essentially like a subquery, with the planner free to optimize across the boundary.

See How It Works

BUSINESS QUESTION

Marketing filters recent converted leads once and summarizes them by source.

idcampaign_idemailcreated_atqualified_atconverted_atlead_scoresourcecountry
3011ana@example.com2024-01-21 09:10:00+002024-01-22 11:00:00+002024-02-02 10:00:00+0086google_adsUS
3021ben@example.com2024-01-24 12:40:00+00NULLNULL52google_adsCA
3032chloe@example.com2024-02-16 08:30:00+002024-02-18 14:20:00+00NULL74emailGB
3043dev@example.com2024-03-20 17:15:00+002024-03-21 09:00:00+002024-04-04 16:00:00+0091linkedinUS
EXAMPLE QUERY
WITH recent_conversions AS (
  SELECT source, converted_at
  FROM marketing.leads
  WHERE converted_at >= TIMESTAMPTZ '2024-02-01 00:00:00+00'
)
SELECT
  source,
  COUNT(*) AS conversions
FROM recent_conversions
GROUP BY source
ORDER BY conversions DESC, source;
RESULT — conversions since February 1 by source
sourceconversions
google_ads1
linkedin1

The fixed boundary includes the converted google_ads and linkedin leads, each with one conversion.

This lesson's practice is part of Pro.

Advanced business practice

Sign up free to try it on a real business scenario