Day 6: Query Planner Improvements: Propagating column stats from CTEs.

Building a Geospatial Data Warehouse with PostgreSQL & PostGIS ~9 min read 60 min hands-on

Day 6: Query Planner Improvements: Propagating Column Stats from CTEs – Seeing Beyond the Fence

Welcome back, fellow architects and engineers! Today, we're diving into a subtle yet profoundly impactful improvement in PostgreSQL's query planner that can make or break the performance of your complex geospatial queries: the ability to propagate column statistics from Common Table Expressions (CTEs).

You've likely used CTEs (those elegant WITH clauses) to break down intricate SQL into readable, logical steps. They're fantastic for modularity, especially in a geospatial context where queries can involve multiple spatial operations, joins, and aggregations. But here's a secret: for a long time, the query planner often treated CTEs like an opaque box, an "optimization fence." It would execute the CTE, get its result, and then try to plan the rest of the query, often without fully understanding the underlying data distribution, selectivity, or cardinality within that CTE.

This is like a general planning a battle without knowing the exact strength or terrain of the enemy forces hiding behind a hill. They'd make a best guess, which sometimes works, but often leads to a disastrous strategy.

The Old Way: Blind Spots and Suboptimal Plans

Imagine you're building a system for a logistics company. You have millions of delivery points and thousands of service regions. A common query might be: "Find all delivery points that fall within a specific high-priority region, and then analyze their average delivery time."

You might write it like this:

sql

In older PostgreSQL versions (prior to 12, with significant improvements in subsequent releases like 17), the planner might have seen high_priority_deliveries and thought, "Okay, this CTE could return many rows, or few. I'll make a conservative estimate." If High-Priority Zone A actually contains only a handful of delivery points, but the planner estimates it'll return hundreds of thousands, it might choose a very expensive join algorithm (like a Hash Join where a Nested Loop would be faster with few rows) or avoid an index scan that would have been perfect. This misestimation, born from a lack of visibility into the CTE's internals, can balloon query times from milliseconds to seconds or even minutes on large geospatial datasets.

The New Way: Peeking Beyond the Fence

PostgreSQL 12+ (and particularly refined in 17) introduced significant enhancements to how the query planner handles non-materialized CTEs. Instead of treating them as black boxes, the planner now attempts to propagate statistics from the CTE's internal queries to the outer query. It essentially "looks inside" the CTE to gather more accurate cardinality estimates, understand data distribution, and infer selectivity of predicates defined within the CTE.

What does this mean for you?

  • Smarter Join Decisions: If a CTE filters down a massive table to a tiny subset using a spatial predicate (like ST_Intersects), the planner will now more accurately estimate that small row count. This allows it to choose efficient join algorithms (e.g., a Nested Loop if the CTE result is small, or a Hash Join if it's still sizable but accurately estimated).

  • Better Index Utilization: The planner can better understand the selectivity of WHERE clauses within the CTE, leading to more intelligent decisions about which indexes (especially PostGIS's GIST spatial indexes) to use, and how to combine them.

  • Reduced Overheads: Less guesswork means fewer suboptimal plans, reducing CPU and I/O overheads.

  • Empowered Readability: You can continue to use CTEs for code organization without fearing a performance penalty due to planner blindness.

Keep going with the full lesson

The rest of this lesson — plus its implementation guide, runnable scripts and source code — is included with the course or an All Access plan. Create a free account to start the free lessons in every course.

Already a member? Sign in