Following tables are used in the examples below:
create table foo (a int);
insert into foo values (2), (3);
create table p (a int) partition by list (a);
create index on p (a);
create table p1 partition of p for values in (1);
create table p2 partition of p for values in (2);
create table p3 partition of p for values in (3);
Dynamic pruning may occur if a query contains comparison of the partition key to a "parameter" whose value is only known during execution. There are two types of execution-time parameters. A query may contain any combination of parameters, type 1 or 2 or both.
If query contains this type of parameter, then pruning can occur before
executing the plan (actually, when initializing the plan) and if pruning
is successful, it is shown using Subplans Removed:
in the EXPLAIN
output.
Example:
prepare example_query as select * from p where a = $1;
In the above query, $1
is this type of parameter.
explain (costs off, timing off, analyze) execute example_query (1);
QUERY PLAN
─────────────────────────────────────────────────────────────────────────────────────
Append
Subplans Removed: 2
-> Bitmap Heap Scan on p1
Recheck Cond: (a = $1)
-> Bitmap Index Scan on p1_a_idx
Index Cond: (a = $1)
Planning Time: 0.019 ms
Execution Time: 0.163 ms
(8 rows)
$1
is assigned the value of 1
and it's fixed for the entire execution of
that query.
(Note that you'll need to run the above query at least 5 times before planner
switches to a generic plan. It's only then that you can see dynamic pruning
in action. I'm running PG 12 development version, where I set plan_cache_mode
for plan cache to always return a generic plan and easily see dynamic pruning
in action.)
If query contains this type of parameter, then pruning occurs every time
its value changes. If a partition is pruned every time, then its subplan
is showed as (never executed)
in the EXPLAIN
output.
Example:
select * from foo f where exists (select * from p where a = f.a);
Here f.a
is the parameter for scanning p
. The above query is actually
implemented using a join between foo
and p
. If p
is the inner table, then
we can use dynamic pruning using a = f.a
condition where f.a
is the
parameter. Pruning is executed for every value of f.a
, that is, for all
rows of foo
.
explain (costs off, timing off, analyze) select * from foo f where exists
(select * from p where a = f.a);
QUERY PLAN
─────────────────────────────────────────────────────────────────────────
Nested Loop Semi Join (actual rows=0 loops=1)
-> Seq Scan on foo f (actual rows=2 loops=1)
-> Append (actual rows=0 loops=2)
-> Bitmap Heap Scan on p1 (never executed)
Recheck Cond: (a = f.a)
-> Bitmap Index Scan on p1_a_idx (never executed)
Index Cond: (a = f.a)
-> Bitmap Heap Scan on p2 (actual rows=0 loops=1)
Recheck Cond: (a = f.a)
-> Bitmap Index Scan on p2_a_idx (actual rows=0 loops=1)
Index Cond: (a = f.a)
-> Bitmap Heap Scan on p3 (actual rows=0 loops=1)
Recheck Cond: (a = f.a)
-> Bitmap Index Scan on p3_a_idx (actual rows=0 loops=1)
Index Cond: (a = f.a)
Planning Time: 3.293 ms
Execution Time: 0.397 ms
(17 rows)
Here p1
's subplan is never executed, because foo
contains only 2
and 3
.
It's also possible for a query to contain both types of parameters, such as in the following example.
prepare example_query as
select * from foo f where exists (select * from p where a = f.a and a <> $1);
Above query contains both types of parameters: = f.a
and <> $1
.
explain (costs off, timing off, analyze) execute example_query (2);
QUERY PLAN
─────────────────────────────────────────────────────────────────────────
Nested Loop Semi Join (actual rows=0 loops=1)
-> Seq Scan on foo f (actual rows=2 loops=1)
-> Append (actual rows=0 loops=2)
Subplans Removed: 1
-> Bitmap Heap Scan on p1 (never executed)
Recheck Cond: (a = f.a)
Filter: (a <> $1)
-> Bitmap Index Scan on p1_a_idx (never executed)
Index Cond: (a = f.a)
-> Bitmap Heap Scan on p3 (actual rows=0 loops=1)
Recheck Cond: (a = f.a)
Filter: (a <> $1)
-> Bitmap Index Scan on p3_a_idx (actual rows=0 loops=1)
Index Cond: (a = f.a)
Planning Time: 4.189 ms
Execution Time: 0.331 ms
(16 rows)
Note that there are both Sublans Removed:
and (never executed)
in this
case.:
Sublans Removed:
exists because of the parameter $1
. When it is
executed with 2
, partition p2
's subplan is removed, because the condition
is a <> $1
.
(never executed)
is shown for p1
, because the parameter f.a
never becomes
1
, because foo
only contain 2
and 3
, so p1
's subplan is never executed.