+ linkend="ddl-inherit">) before attempting to implement
+ partitioning.
+
+
+ Partitioning can provide several benefits:
+
+
+ Query performance can be improved dramatically for certain kinds
+ of queries without the need to maintain costly indexes.
+
+
+
+
+ Insert performance can be improved by breaking down a large
+ index into multiple pieces. When an index no longer fits easily
+ in memory, both read and write operations on the index take
+ progressively more disk accesses.
+
+
+
+
+ Bulk deletes may be avoided altogether by simply removing one of the
+ partitions, if that requirement is planned into the partitioning design.
+
+
+
+
+ Seldom-used data can be migrated to cheaper and slower storage media.
+
+
+
+
+ The benefits will normally be worthwhile only when a data table would
+ otherwise be very large. That is for you to judge, though would not
+ usually be lower than the size of physical RAM on the database server.
+
+
+ In
PostgreSQL &version;, the following
+ partitioning types are supported:
+
+
+
+ "Range Partitioning" where the table is partitioned along a
+ "range" defined by a single column or set of columns, with no
+ overlap between partitions. Examples might be a date range or a
+ range of identifiers for particular business objects.
+
+
+
+
+ "List Partitioning" where the table is partitioned by
+ explicitly listing which values relate to each partition.
+
+
+
+
+ Hash partitioning is not currently supported.
+
+
+
+
+
Implementing Partitioning
+
+ Partitioning a table is a straightforward process. There
+ are a wide range of options for you to consider, so judging exactly
+ when and how to implement partitioning is a more complex topic. We
+ will address that complexity primarily through the examples in this
+ section.
+
+
+ To use partitioning, do the following:
+
+
+ Create the master
table, from which all of the
+ partitions will inherit.
+
+ This table will contain no data. Do not define any
+ constraints or keys on this table, unless you intend them to
+ be applied equally to all partitions.
+
+
+
+
+ Create several child
tables that inherit from
+ the master table.
+
+
+ We will refer to the child tables as partitions, though they
+ are in every way just normal
PostgreSQL>
+ tables.
+
+
+
+
+ Add table constraints to define the allowed values in each partition.
+
+ Only clauses of the form [COLUMN] [OPERATOR] [CONSTANT(s)] will be used
+ for constraint exclusion. Simple examples would be:
+CHECK ( x = 1 )
+CHECK ( county IN ('Oxfordshire','Buckinghamshire','Warwickshire'))
+CHECK ( outletID BETWEEN 1 AND 99 )
+
+
+ These can be linked together with boolean operators AND and OR to
+ form complex constraints. Note that there is no difference in syntax
+ between Range and List Partitioning mechanisms; those terms are
+ descriptive only. Ensure that the set of values in each child table
+ do not overlap.
+
+
+
+
+ Add any other indexes you want to the partitions, bearing in
+ mind that it is always more efficient to add indexes after
+ data has been bulk loaded.
+
+
+
+
+ Optionally, define a rule or trigger to redirect modifications
+ of the master table to the appropriate partition.
+
+
+
+
+
+
+ For example, suppose we are constructing a database for a large
+ ice cream company. The company measures peak temperatures every
+ day as well as ice cream sales in each region. They have two
+ tables:
+
+CREATE TABLE cities (
+ id int not null,
+ name text not null,
+ altitude int -- in feet
+);
+
+CREATE TABLE measurement (
+ city_id int not null,
+ logdate date not null,
+ peaktemp int,
+ unitsales int
+);
+
+
+ To reduce the amount of old data that needs to be stored, we
+ decide to only keep the most recent 3 years worth of data. At the
+ beginning of each month we remove the oldest month's data.
+
+
+ Most queries just access the last week, month or quarter's data,
+ since we need to keep track of sales. As a result we have a large table,
+ yet only the most frequent 10% is accessed. Most of these queries
+ are online reports for various levels of management. These queries access
+ much of the table, so it is difficult to build enough indexes and at
+ the same time allow us to keep loading all of the data fast enough.
+ Yet, the reports are online so we need to respond quickly.
+
+
+ In this situation we can use partitioning to help us meet all of our
+ different requirements for the measurements table. Following the
+ steps outlined above, partitioning can be enabled as follows:
+
+
+
+
+ The measurement table is our master table.
+
+
+
+
+ Next we create one partition for each month using inheritance:
+
+CREATE TABLE measurement_yy04mm02 ( ) INHERITS (measurement);
+CREATE TABLE measurement_yy04mm03 ( ) INHERITS (measurement);
+...
+CREATE TABLE measurement_yy05mm11 ( ) INHERITS (measurement);
+CREATE TABLE measurement_yy05mm12 ( ) INHERITS (measurement);
+CREATE TABLE measurement_yy06mm01 ( ) INHERITS (measurement);
+
+
+ Each of the partitions are complete tables in their own right,
+ but they inherit their definition from the measurement table.
+
+
+ This solves one of our problems: deleting old data. Each
+ month, all we need to do is perform a DROP
+ TABLE on the oldest table and create a new table to
+ insert into.
+
+
+
+
+ We now add non-overlapping table constraints, so that our
+ table creation script becomes:
+
+CREATE TABLE measurement_yy04mm02 (
+ CHECK ( logdate >= DATE '2004-02-01' AND logdate < DATE '2004-03-01' )
+ ) INHERITS (measurement);
+CREATE TABLE measurement_yy04mm03 (
+ CHECK ( logdate >= DATE '2004-03-01' AND logdate < DATE '2004-04-01' )
+ ) INHERITS (measurement);
+...
+CREATE TABLE measurement_yy05mm11 (
+ CHECK ( logdate >= DATE '2005-11-01' AND logdate < DATE '2005-12-01' )
+ ) INHERITS (measurement);
+CREATE TABLE measurement_yy05mm12 (
+ CHECK ( logdate >= DATE '2005-12-01' AND logdate < DATE '2006-01-01' )
+ ) INHERITS (measurement);
+CREATE TABLE measurement_yy06mm01 (
+ CHECK ( logdate >= DATE '2006-01-01' AND logdate < DATE '2006-02-01' )
+ ) INHERITS (measurement);
+
+
+
+
+
+ We choose not to add further indexes at this time.
+
+
+
+
+ Data will be added each day to the latest partition. This
+ allows us to set up a very simple rule to insert data. We must
+ redefine this each month so that it always points to the
+ current partition.
+
+CREATE OR REPLACE RULE measurement_current_partition AS
+ON INSERT
+TO measurement
+DO INSTEAD
+ INSERT INTO measurement_yy06mm01 VALUES ( NEW.city_id,
+ NEW.logdate,
+ NEW.peaktemp,
+ NEW.unitsales );
+
+
+ We might want to insert data and have the server automatically
+ locate the partition into which the row should be added. We
+ could do this with a more complex set of rules as shown below.
+
+CREATE RULE measurement_insert_yy04mm02 AS
+ON INSERT
+TO measurement WHERE
+ ( logdate >= DATE '2004-02-01' AND logdate < DATE '2004-03-01' )
+DO INSTEAD
+ INSERT INTO measurement_yy04mm02 VALUES ( NEW.city_id,
+ NEW.logdate,
+ NEW.peaktemp,
+ NEW.unitsales );
+...
+CREATE RULE measurement_insert_yy05mm12 AS
+ON INSERT
+TO measurement WHERE
+ ( logdate >= DATE '2005-12-01' AND logdate < DATE '2006-01-01' )
+DO INSTEAD
+ INSERT INTO measurement_yy05mm12 VALUES ( NEW.city_id,
+ NEW.logdate,
+ NEW.peaktemp,
+ NEW.unitsales );
+CREATE RULE measurement_insert_yy06mm01 AS
+ON INSERT
+TO measurement WHERE
+ ( logdate >= DATE '2006-01-01' AND logdate < DATE '2006-02-01' )
+DO INSTEAD
+ INSERT INTO measurement_yy06mm01 VALUES ( NEW.city_id,
+ NEW.logdate,
+ NEW.peaktemp,
+ NEW.unitsales );
+
+
+ Note that the WHERE clause in each rule
+ exactly matches those used for the CHECK
+ constraints on each partition.
+
+
+
+
+
+ As we can see, a complex partitioning scheme could require a
+ substantial amount of DDL. In the above example we would be
+ creating a new partition each month, so it may be wise to write a
+ script that generates the required DDL automatically.
+
+
+ The following caveats apply:
+
+
+ There is currently no way to specify that all of the
+ CHECK constraints are mutually
+ exclusive. Care is required by the database designer.
+
+
+
+
+ There is currently no way to specify that rows may not be
+ inserted into the master table. A CHECK
+ constraint on the master table will be inherited by all child
+ tables, so that cannot not be used for this purpose.
+
+
+
+
+ For some datatypes you must explicitly coerce the constant values
+ into the datatype of the column. The following constraint will
+ work if x is an INTEGER datatype, but not if x is BIGINT datatype.
+CHECK ( x = 1 )
+
+ For BIGINT we must use a constraint like:
+CHECK ( x = 1::bigint )
+
+ The issue is not restricted to BIGINT datatypes but can occur whenever
+ the default datatype of the constant does not match the datatype of
+ the column to which it is being compared.
+
+
+
+
+ Partitioning can also be arranged using a UNION
+ ALL view:
+
+CREATE VIEW measurement AS
+ SELECT * FROM measurement_yy04mm02
+UNION ALL SELECT * FROM measurement_yy04mm03
+...
+UNION ALL SELECT * FROM measurement_yy05mm11
+UNION ALL SELECT * FROM measurement_yy05mm12
+UNION ALL SELECT * FROM measurement_yy06mm01;
+
+
+ However, constraint exclusion is currently not supported for
+ partitioned tables defined in this manner.
+
+
+
+
+
+
+
+
Constraint Exclusion in Queries
+
+ Partitioning can be used to improve query performance when used in
+ conjunction with constraint exclusion. As an example:
+
+SET constraint_exclusion=true;
+SELECT count(*) FROM measurement WHERE logdate >= DATE '2006-01-01';
+
+
+ Without constraint exclusion, the above query would scan each of
+ the partitions of the measurement table. With constraint
+ exclusion, the planner will examine each of the constraints and
+ try to prove that each of the partitions needs to be involved in
+ the query. If the planner is able to refute that for any
+ partition, it excludes the partition from the query plan.
+
+
+ You can use the EXPLAIN> command to show the difference
+ between a plan with constraint_exclusion> on and a plan
+ with it off.
+
+SET constraint_exclusion=false;
+EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2006-01-01';
+
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------
+ Aggregate (cost=158.66..158.68 rows=1 width=0)
+ -> Append (cost=0.00..151.88 rows=2715 width=0)
+ -> Seq Scan on measurement (cost=0.00..30.38 rows=543 width=0)
+ Filter: (logdate >= '2006-01-01'::date)
+ -> Seq Scan on measurement_yy04mm02 measurement (cost=0.00..30.38 rows=543 width=0)
+ Filter: (logdate >= '2006-01-01'::date)
+ -> Seq Scan on measurement_yy04mm03 measurement (cost=0.00..30.38 rows=543 width=0)
+ Filter: (logdate >= '2006-01-01'::date)
+...
+ -> Seq Scan on measurement_yy05mm12 measurement (cost=0.00..30.38 rows=543 width=0)
+ Filter: (logdate >= '2006-01-01'::date)
+ -> Seq Scan on measurement_yy06mm01 measurement (cost=0.00..30.38 rows=543 width=0)
+ Filter: (logdate >= '2006-01-01'::date)
+
+
+ Now when we enable constraint exclusion, we get a significantly
+ reduced plan but the same result set:
+
+SET constraint_exclusion=true;
+EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2006-01-01';
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------
+ Aggregate (cost=63.47..63.48 rows=1 width=0)
+ -> Append (cost=0.00..60.75 rows=1086 width=0)
+ -> Seq Scan on measurement (cost=0.00..30.38 rows=543 width=0)
+ Filter: (logdate >= '2006-01-01'::date)
+ -> Seq Scan on measurement_yy06mm01 measurement (cost=0.00..30.38 rows=543 width=0)
+ Filter: (logdate >= '2006-01-01'::date)
+
+
+ Don't forget that you still need to run ANALYZE
+ on each partition individually. A command like this
+ANALYZE measurement;
+
+
+ only affects the master table.
+
+
+ No indexes are required to use constraint exclusion. The
+ partitions should be defined with appropriate CHECK>
+ constraints. These are then compared with the predicates of the
+ SELECT> query to determine which partitions must be
+ scanned.
+
+
+ The following caveats apply to this release:
+
+
+ Constraint exclusion only works when the query directly matches
+ a constant. A constant bound to a parameterised query will not
+ work in the same way since the plan is fixed and would need to
+ vary with each execution. Also, stable constants such as
+ CURRENT_DATE may not be used, since these are
+ constant only for during the execution of a single query. Join
+ conditions will not allow constraint exclusion to work either.
+
+
+
+
+ UPDATEs and DELETEs against the master table do not perform
+ constraint exclusion.
+
+
+
+
+ All constraints on all partitions of the master table are considered for
+ constraint exclusion, so large numbers of partitions are likely to
+ increase query planning time considerably.
+
+
+
+
+
+
+
+
-
+
Privileges
schema. To allow that, the CREATE privilege on
the schema needs to be granted. Note that by default, everyone
has CREATE and USAGE privileges on
- the schema
+ the schema
public. This allows all users that are able to
connect to a given database to create objects in its
public schema. If you do