skip to content

What is partition pruning in a partitioned table, what does a query have to contain for the database to do it, and how is it different from using an index?

level: juniorimportance: must knowfreq 62%

answer

  1. bounds are a proof, not just routing
  2. predicate must be on the partition key
  3. hash partitions prune on equality only
  4. Append node lists surviving partitions only
  5. pruning removes tables, indexes narrow within a table

basics

~20 s

Pruning is the database eliminating partitions that cannot contain matching rows, using each partition's declared bounds. It needs a predicate on the partition key. An index narrows the search inside a table; pruning removes whole tables from the plan before any scan happens.

solid answer

~60 s

Every partition carries a declared bound: this one holds January, that one holds February. If a query restricts the partition key, the planner can compare the predicate to those bounds and skip any partition whose bound cannot contain a matching row. That is partition pruning, and it is the main reason partitioning helps query performance at all. The requirement is a predicate on the partition key itself. Filtering on some other column gives the planner nothing to compare against, so every partition is scanned; a partitioned table queried without a key predicate is generally slower than the same data unpartitioned, because of per-partition planning and locking overhead. Pruning is not an index. An index reduces work inside one table by pointing at the matching rows. Pruning removes entire tables from the plan before any scanning happens, and the two compose: after pruning to one month's partition, the plan still uses that partition's index to find rows within it. In a plan, pruning shows up as an Append node with only the surviving partitions as children.

code

text · 4 lines
text
Append
  ->  Index Scan using events_2026_03_occurred_at_idx on events_2026_03
        Index Cond: (occurred_at >= '2026-03-01' AND occurred_at < '2026-04-01')
  (the other 11 partitions do not appear: they were pruned)

go deeper

for a junior

Define it plainly: partitions declare which key values they hold, so a filter on that key lets the database skip the rest, and this only works if the query filters on the partition key.

for a middle

Add the shapes that prune per partitioning strategy, the composition with per-partition indexes, and what the Append node in a plan tells you.

for a senior

Contrast with the older constraint-exclusion mechanism, note enable_partition_pruning, and quantify the overhead of many partitions for queries that cannot prune.

for a principal

Tie the partition key choice to the workload's dominant access paths, and judge granularity by weighing I/O saved against per-partition planning, locking and maintenance cost.

## The idea A partitioned table is one logical table made of several physical tables, each declared to hold a specific slice of the key space: a range of dates, a list of region codes, a hash bucket. Those declarations are not just routing rules for inserts, they are guarantees the optimizer can reason with. If a partition is declared to hold only January and the query asks for rows in March, that partition provably contains nothing of interest and can be dropped from the plan without being opened. That elimination is partition pruning. The payoff is proportional: a table split into 36 monthly partitions where a query touches one month reads roughly 1/36th of the data, and equally importantly touches only that partition's indexes, which are individually far smaller and better cached than one global index would be. ## What the query must contain Pruning is driven by predicates on the **partition key**, that is, the expression the table was partitioned by. Some shapes work naturally: - range partitioning by a timestamp prunes for equality, comparisons and BETWEEN on that timestamp; - list partitioning by a region code prunes for equality and IN lists on that code; - hash partitioning prunes only for equality, because a hash destroys ordering, so a range comparison on a hash-partitioned key cannot exclude anything. If the query filters only on other columns, no pruning happens and every partition is scanned. This is the single most common disappointment with partitioning: teams partition a table, then run queries that never mention the partition key, and performance gets worse rather than better because the planner now handles many relations and takes a lock on each one. ## How it differs from an index They operate at different levels and are complementary. - An **index** is a data structure inside one table that lets a scan jump to matching rows instead of reading everything. It reduces work within a relation. - **Pruning** is an optimizer and executor decision that removes whole relations from consideration. It happens before any data is read from those partitions. A good plan on a partitioned table usually does both: prune to the handful of partitions that could match, then use each surviving partition's index for the rest of the predicate. Saying "partitioning replaces indexing" is wrong; partitioning changes which index is consulted and how big it is, but selective lookups still need indexes. ## How you see it In PostgreSQL a query over a partitioned table normally plans as an **Append** (or Merge Append) node with one child per partition. Pruning shows up as children that simply are not there: only the surviving partitions appear. If exactly one partition survives, the Append disappears entirely and the plan shows that partition's scan node directly, which is a strong visual signal that pruning worked. Other engines print an equivalent, such as a partitions-accessed list in the plan output. ## Related mechanisms and terminology Older PostgreSQL versions eliminated child tables using CHECK constraints on inheritance hierarchies, a planner-only mechanism called **constraint exclusion**, controlled by the `constraint_exclusion` setting. Modern declarative partitioning uses partition bounds directly, is faster, handles more predicate shapes, and is controlled by `enable_partition_pruning` (on by default). Turning that setting off is a debugging tool, and seeing it off in production is a bug. Distinguishing the two mechanisms is a nice signal of depth, but the concept is the same: prove a relation cannot match, then skip it. ## A caution about granularity Pruning improves with more partitions only up to a point. Each partition adds planning work, catalog entries and a lock to acquire per query, so thousands of tiny partitions can cost more in planning than they save in I/O, especially for short OLTP queries. The useful mental model is: pruning turns a table scan into a scan of the relevant slice, and the slice should be big enough to be worth the bookkeeping.

  • A table is partitioned by hash on customer_id. Which predicates on customer_id can prune, and which cannot?
    Equality and IN lists can prune, because the engine hashes each supplied value and keeps only the matching buckets. Range comparisons such as customer_id > 1000 cannot prune anything, because hashing destroys ordering, so qualifying values can be spread across every bucket. That is why hash partitioning suits point lookups and even data distribution, not range scans.
  • If a query on a partitioned table has no predicate on the partition key, is it slower or faster than the same data in a single unpartitioned table?
    Usually slightly slower. Every partition must be planned and locked, the plan is larger, and per-partition overhead adds up, while the amount of data read is the same. Some of that is recovered by parallelism or partition-wise aggregation on large scans, but for short queries the overhead dominates. This is why the partition key should match the way the workload actually filters.

Bounds are labels on filing drawers: pruning is not opening the drawers whose labels cannot match; an index is the tab dividers inside the one drawer you did open.

saying these in an interview costs you the question

  • Believing partitioning speeds up every query, even those that never mention the partition key.
  • Claiming partitioning removes the need for indexes on the partitions.
  • Assuming range predicates prune a hash-partitioned table.
  • Thinking pruning is the same mechanism as an index scan, just at table level.

context