← Blog · Engineering & SRE · September 20, 2026 · 7 min read · By Operate Technical Staff

Your Query Still Touched Every Partition: A Cross-Engine Decision Procedure for Proving Whether Pruning Actually Happened

Partition pruning fails silently: correct rows, no error, forty times the I/O. How to prove which partitions your query read, tell a bad predicate from a bad key, and detect the regression.

Your Query Still Touched Every Partition: A Cross-Engine Decision Procedure for Proving Whether Pruning Actually Happened

Your Query Still Touched Every Partition: A Cross-Engine Decision Procedure for Proving Whether Pruning Actually Happened

TL;DR Partition pruning is a query optimization technique that skips irrelevant data partitions to reduce I/O and latency. If your query returns correct rows but scans the entire table anyway, you likely have a non-sargable predicate (like a type mismatch) or a structural mismatch between your partition key and access patterns.

The query is correct. It just read forty times more than it needed to.

When partition pruning fails, it fails silently. There is no error message, no warning in the logs, and the result set is perfectly accurate. In PostgreSQL and MySQL, this manifests as a slow-burn latency regression. In BigQuery, Snowflake, or Athena, it surfaces as a "bytes scanned" line item on a cloud invoice—often routed to a finance lead who cannot read a query plan.

As noted in the recent practitioner writeup "I Partitioned 1M Rows and the Query Still Touched Every Partition" (Medium, 2026-09-19), the failure is particularly dangerous because it is invisible at small scales. The author observed that their query was still "fast" at a million rows despite scanning every partition. It is only when that table hits a billion rows that the silent lack of pruning becomes a production-ending event.

First question: is this a predicate problem or a partition key problem?

Before diving into syntax, you must identify which fork of the failure you are facing.

  1. Predicate Problem: Your table is partitioned on created_at, and your query filters on created_at, but the optimizer cannot prove the filter matches the bounds. This is a software defect that can be fixed with a rewrite.
  2. Partition Key Problem: Your table is partitioned on created_at, but your top ten most frequent queries filter exclusively on user_id. No amount of query tuning will fix this. The schema is structurally incompatible with the workload.

The One-Line Test: List your top ten queries by call count and the columns they filter on. Compare this to your partition key. If the key isn't in the filter list, stop looking at predicates. You need to repartition or add a secondary access path.

Pruning is a proof, not an optimization you enable

To troubleshoot effectively, you must adopt the engine's mental model: Pruning is a proof. An engine only skips a partition when it can mathematically prove, based on metadata and query constants, that no matching row could possibly exist in that slice.

Key Takeaway: Partition pruning is not an "optional" boost you toggle; it is the result of the optimizer successfully proving that specific data blocks are irrelevant to your query filters.

Pruning occurs at three stages:

According to the official PostgreSQL documentation, both enable_partition_pruning and constraint_exclusion settings affect this behavior, though the former is the modern standard for partitioned tables.

Prove it: reading the plan in each engine

To diagnose a failure, you must know what "success" looks like in your specific engine.

Engine How to Check Success Signal Failure Signal
PostgreSQL EXPLAIN (ANALYZE, BUFFERS) Child nodes absent from Append; "Subplans Removed" A massive Append node containing every child partition
MySQL 8.4 EXPLAIN SELECT ... The partitions column shows specific IDs The partitions column shows p0,p1,p2...pN
BigQuery Dry-run estimate "This query will process 10 MB" "This query will process 2 TB" (on a partitioned table)
Snowflake Query Profile "Partitions scanned" << "Partitions total" Scanned and Total counts are identical
Databricks EXPLAIN EXTENDED PartitionFilters present in the Scan node Only PushedFilters (non-partition filters) present

The predicate failures, in the order they actually occur

If you have the right key but pruning still fails, one of these common pitfalls is usually responsible:

1. Function or Cast on the Partition Key

If your key is created_at (a timestamp), and you write WHERE DATE(created_at) = '2026-09-20', pruning will fail in most engines. The optimizer sees a function call, not a raw column, and cannot map it to the partition bounds. The Fix: Rewrite to a range: WHERE created_at >= '2026-09-20 00:00:00' AND created_at < '2026-09-21 00:00:00'.

2. Type Mismatch (The ORM Trap)

You might pass a string literal '123' to a numeric partition key. While the query returns the correct rows due to implicit casting, the optimizer may refuse to prune because the cast makes the predicate non-sargable. This is common when an ORM driver maps a parameter to the wrong wire type.

3. OR Branches and Inclusivity

A query like WHERE key = 1 OR other_col = 'a' often forces a full scan because the engine must check other_col across all partitions. In PostgreSQL, using UNION ALL between two pruned queries is often more efficient than a single OR.

The correction: statistics do not decide pruning

A common misconception, repeated on several community troubleshooting pages, is that stale statistics cause partition pruning to fail. This is technically incorrect. As documented by both Oracle and PostgreSQL, pruning is decided based on partition bounds (the DDL defining the ranges), not the selectivity statistics gathered by ANALYZE.

However, stale statistics can cause the optimizer to choose a join strategy (like a Hash Join instead of a Nested Loop) that prevents dynamic partition pruning from triggering. The pruning didn't "fail" to see the bounds; the optimizer simply chose a path where pruning wasn't applicable.

Detecting that a query stopped pruning, without waiting for the bill

Detection is the gap in most SRE stacks. Because pruning failures don't error, you need a regression detector based on I/O signatures.

Why this matters: The regression causes

Why does a query that pruned yesterday stop pruning today?

What to do once you know which case you are in

If it’s a Predicate Case, rewrite the query to avoid functions on the key and ensure type alignment. If it’s a Key-Mismatch Case, you have three honest choices: repartition the table, add a secondary index (which is an I/O trade-off), or accept the scan and adjust your budget.

At Operate, we see these silent regressions as the most dangerous kind of technical debt. The signal exists in your metadata—pg_stat_statements and query profiles already have the data—but humans aren't paid to watch every fingerprint for I/O drifts. Operate watches these signatures proactively, opening an investigation when a query's "blocks per call" spikes while its results stay flat. Unlike tools that might automatically repartition your data, Operate provides the evidence and a suggested fix as a PR, keeping the final decision in the hands of the engineer.

FAQ

What is partition pruning? It is a performance optimization where the database skip-reads entire partitions that cannot possibly contain data matching the query's WHERE clause.

What is the difference between predicate pushdown and partition pruning? Predicate pushdown moves filters closer to the data source (like reading a Parquet file), while partition pruning uses table metadata to avoid opening the file or partition entirely.

How do I force partition pruning in PostgreSQL? Ensure enable_partition_pruning is on and that your filters use the exact column and data type defined in your partition key without wrapping them in functions.

Sources & further reading

#postgresql#database-performance#sre#sql-optimization#bigquery#snowflake