<para>
Indexes and foreign key constraints apply to single tables and not
to their inheritance children, hence they have some
<link linkend="ddl-inherit-caveats">caveats</link> to be aware of.
</para>
</listitem>
<listitem>
<para>
The schemes shown here assume that the values of a row's key column(s)
never change, or at least do not change enough to require it to move to another partition.
An <command>UPDATE</command> that attempts
to do that will fail because of the <literal>CHECK</literal> constraints.
If you need to handle such cases, you can put suitable update triggers
on the child tables, but it makes management of the structure
much more complicated.
</para>
</listitem>
<listitem>
<para>
Manual <command>VACUUM</command> and <command>ANALYZE</command>
commands will automatically process all inheritance child tables. If
this is undesirable, you can use the <literal>ONLY</literal> keyword.
A command like:
<programlisting>
ANALYZE ONLY measurement;
</programlisting>
will only process the root table.
</para>
</listitem>
<listitem>
<para>
<command>INSERT</command> statements with <literal>ON CONFLICT</literal>
clauses are unlikely to work as expected, as the <literal>ON CONFLICT</literal>
action is only taken in case of unique violations on the specified
target relation, not its child relations.
</para>
</listitem>
<listitem>
<para>
Triggers or rules will be needed to route rows to the desired
child table, unless the application is explicitly aware of the
partitioning scheme. Triggers may be complicated to write, and will
be much slower than the tuple routing performed internally by
declarative partitioning.
</para>
</listitem>
</itemizedlist>
</para>
</sect3>
</sect2>
<sect2 id="ddl-partition-pruning">
<title>Partition Pruning</title>
<indexterm>
<primary>partition pruning</primary>
</indexterm>
<para>
<firstterm>Partition pruning</firstterm> is a query optimization technique
that improves performance for declaratively partitioned tables.
As an example:
<programlisting>
SET enable_partition_pruning = on; -- the default
SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
</programlisting>
Without partition pruning, the above query would scan each of the
partitions of the <structname>measurement</structname> table. With
partition pruning enabled, the planner will examine the definition
of each partition and prove that the partition need not
be scanned because it could not contain any rows meeting the query's
<literal>WHERE</literal> clause. When the planner can prove this, it
excludes (<firstterm>prunes</firstterm>) the partition from the query
plan.
</para>
<para>
By using the EXPLAIN command and the <xref
linkend="guc-enable-partition-pruning"/> configuration parameter, it's
possible to show the difference between a plan for which partitions have
been pruned and one for which they have not. A typical unoptimized
plan for this type of table setup is:
<programlisting>
SET enable_partition_pruning = off;
EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
QUERY PLAN
-------------------------------------------------------------------&zwsp;----------------
Aggregate (cost=188.76..188.77 rows=1 width=8)
-> Append (cost=0.00..181.05 rows=3085 width=0)
-> Seq Scan on measurement_y2006m02 (cost=0.00..33.12 rows=617 width=0)
Filter: (logdate >= '2008-01-01'::date)
-> Seq Scan on measurement_y2006m03 (cost=0.00..33.12 rows=617 width=0)
Filter: (logdate