On Mon, 2005-10-31 at 02:46 +0000, Simon Riggs wrote:
> I've been working on some docs for Constraining Exclusion & Partitioning
> for some time now. Deadlines seem to be looming, or may even have
> passed, so it seems sensible to submit what I have now. 

> Many thanks to Josh Berkus for providing the numbered section on
> implementation process, which was the starting point I'd been looking
> for to describe everything else.

I believe this is now complete and ready for application.

- passes sgml make against cvstip
- spellchecked
- all code executed correctly against RC1

Comments please? Apart from the obvious, so why did it take you so long.
Apologies to the translators.

Best Regards, Simon Riggs

Index: ddl.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ddl.sgml,v
retrieving revision 1.45
diff -c -c -r1.45 ddl.sgml
*** ddl.sgml	23 Oct 2005 19:29:49 -0000	1.45
--- ddl.sgml	31 Oct 2005 22:36:26 -0000
***************
*** 398,403 ****
--- 398,410 ----
      ensure that a column does not contain null values, the not-null
      constraint described in the next section can be used.
     </para>
+ 
+     <para>
+      Check constraints can also be used to enhance performance with
+      very large tables, when used in conjunction with the 
+      <xref linkend="guc-constraint-exclusion"> parameter. 
+      This is discussed in more detail in <xref linkend="ce-partitioning">
+    </para>
    </sect2>
  
    <sect2>
***************
*** 1040,1052 ****
   <sect1 id="ddl-inherit">
    <title>Inheritance</title>
  
!   <remark>This section needs to be rethought.  Some of the
!   information should go into the following chapters.</remark>
  
    <para>
!    Let's create two tables. The capitals  table  contains
!    state  capitals  which  are also cities. Naturally, the
!    capitals table should inherit from cities.
  
  <programlisting>
  CREATE TABLE cities (
--- 1047,1081 ----
   <sect1 id="ddl-inherit">
    <title>Inheritance</title>
  
!    <indexterm>
!     <primary>not-null constraint</primary>
!    </indexterm>
! 
!    <indexterm>
!     <primary>constraint</primary>
!     <secondary>NOT NULL</secondary>
!    </indexterm>
! 
!   <para>
!    <productname>PostgreSQL</productname> was the first DBMS to introduce
!    inheritance, one of its many object-relational features.
!    <productname>PostgreSQL</productname> implements table
!    inheritance which can be a useful tool for database designers. 
!    The SQL:2003 standard optionally defines type inheritance which differs
!    in many respects from the features described here.
!   </para>
  
    <para>
!    Let's start with an example: 
!    We're trying to build a data model for cities, but we have a problem.
!    Each state has many cities, but only one capital. We want to be able
!    to quickly retrieve the capital city for any particular state. We
!    can solve this problem by creating two tables. The capitals table contains
!    state  capitals, then we have another table for cities that aren't capitals.
!    What happens when we want to ask for data about a city, regardless of
!    whether it is a capital or not? We can use the inheritance feature to
!    help resolve this problem for us. We define the capitals table so that
!    it inherits from cities.
  
  <programlisting>
  CREATE TABLE cities (
***************
*** 1062,1077 ****
  
     In this case, a row of capitals <firstterm>inherits</firstterm> all
     attributes (name, population, and altitude) from its parent, cities.  State
!    capitals have an extra attribute, state, that shows their state.  In
!    <productname>PostgreSQL</productname>, a table can inherit from zero or
     more other tables, and a query can reference either all rows of a table or
     all rows of a table plus all of its descendants.
- 
-    <note>
-     <para>
-      The inheritance hierarchy is actually a directed acyclic graph.
-     </para>
-    </note>
    </para>
  
    <para>
--- 1091,1102 ----
  
     In this case, a row of capitals <firstterm>inherits</firstterm> all
     attributes (name, population, and altitude) from its parent, cities.  State
!    capitals have an extra attribute, state, that shows their state.  
!   </para>
!   <para>
!    In <productname>PostgreSQL</productname>, a table can inherit from zero or
     more other tables, and a query can reference either all rows of a table or
     all rows of a table plus all of its descendants.
    </para>
  
    <para>
***************
*** 1133,1163 ****
    </para>
  </note>
    
!   <note>
!    <title>Deprecated</title> 
!    <para>
!      In previous versions of <productname>PostgreSQL</productname>, the
!      default behavior was not to include child tables in queries. This was
!      found to be error prone and is also in violation of the SQL:2003
!      standard. Under the old syntax, to get the sub-tables you append
!      <literal>*</literal> to the table name.
!      For example
! <programlisting>
! SELECT * from cities*;
! </programlisting>
!      You can still explicitly specify scanning child tables by appending
!      <literal>*</literal>, as well as explicitly specify not scanning child tables by
!      writing <quote>ONLY</quote>.  But beginning in version 7.1, the default
!      behavior for an undecorated table name is to scan its child tables
!      too, whereas before the default was not to do so.  To get the old
!      default behavior, set the configuration option
!      <literal>SQL_Inheritance</literal> to off, e.g.,
! <programlisting>
! SET SQL_Inheritance TO OFF;
! </programlisting>
!      or add a line in your <filename>postgresql.conf</filename> file.
!    </para>
!   </note>
  
    <para>
    In some cases you may wish to know which table a particular row
--- 1158,1180 ----
    </para>
  </note>
    
!   <para>
!    Inheritance does not automatically propogate data from INSERT or COPY 
!    commands to other tables in the inheritance hierarchy. Inheritance
!    does not influence these commands.
!    In our example, the following INSERT statement will fail.
! <programlisting>
! INSERT INTO cities 
! (name, population, altitude, state)
! VALUES ('New York', NULL, NULL, 'NY');
! </programlisting>         
!    We might hope that the data would be somehow routed to the capitals table,
!    though this does not happen. If the child has no locally defined
!    columns, then it is possible to route data from the parent to the child
!    using a RULE, see <xref linkend="rules-update">. 
!    This is not possible with the above INSERT statement
!    because the state column does not exist on both parent and child tables.
!   </para>
  
    <para>
    In some cases you may wish to know which table a particular row
***************
*** 1204,1220 ****
    </para>
  
    <para>
!    A table can inherit from more than one parent table, in which case it has
!    the union of the columns defined by the parent tables (plus any columns
!    declared specifically for the child table).
    </para>
  
    <para>
!    A serious limitation of the inheritance feature is that indexes (including
!    unique constraints) and foreign key constraints only apply to single
!    tables, not to their inheritance children.  This is true on both the
!    referencing and referenced sides of a foreign key constraint.  Thus,
!    in the terms of the above example:
  
     <itemizedlist>
      <listitem>
--- 1221,1287 ----
    </para>
  
    <para>
!    As shown above, a child table may locally define columns as 
!    well as inheriting them from their parents. 
!    However, polymorphism of table datatypes is not supported: a locally defined
!    column cannot override the datatype of an inherited column of the same name.
!   </para>
! 
!   <para>
!    A table can inherit from a table that has itself inherited from 
!    other tables. A table can also inherit from more than one parent 
!    table, in which case it has the union of the columns defined by the parent 
!    tables (plus any columns declared locally for the child table). 
!    Inherited columns with duplicate names and datatypes will be merged so
!    that only a single column is stored. An inheritance definition that 
!    results in a column with more than one datatype would not succeed. 
!    The full inheritance hierarchy is actually a directed acyclic graph of
!    arbitrary complexity. You should note that complex inheritance hierarchies
!    will cause a corresponding increase in parsing time for your queries. 
!    Direct access to the specific table your query requires will always
!    provide the best performance.
    </para>
  
    <para>
!    Table inheritance can currently only be defined using the 
!    <xref linkend="SQL-CREATETABLE"> statement.
!    The related statement CREATE TABLE ... AS SELECT does
!    not allow the specification of an inheritance link. The ALTER TABLE statement
!    does not currently support the INHERITS() clause. There is no
!    way to add an inheritance link to make an existing table into a child
!    table. Similarly, there is no way to remove an inheritance link from a 
!    child table, once defined, other than using DROP TABLE on the child table.
!    A parent table cannot be dropped while any of its children remain. If you 
!    wish to remove a table and all of its descendants, then you can do so 
!    using the CASCADE option of the <xref linkend="SQL-DROPTABLE"> statement.
!   </para>
! 
!   <para>
!    Check constraints can be defined on tables within an inheritance
!    hierarchy. All check constraints on a parent table are automatically
!    inherited by all of their children. Note that is currently possible to 
!    inherit mutually exclusive check constraints, but that definition quickly 
!    shows itself since all attempted row inserts will be rejected.
!   </para>
! 
!   <para>
!    <xref linkend="SQL-ALTERTABLE"> will propogate any changes in data 
!    definition on columns or check constraints down the inheritance hierarchy. 
!    Again, dropping columns or constraints on parent tables is only possible 
!    when using the CASCADE option. ALTER TABLE follows the same rules for 
!    duplicate column merging and rejection that apply during CREATE TABLE.
!   </para>
! 
!   <para>
!    Both parent and child tables can have primary and foreign keys
!    defined for them, so that they can take part normally on both the 
!    referencing and referenced sides of a foreign key constraint. Indexes
!    may be defined on any of these columns whether or not they are inherited.
!    However, a serious current limitation of the inheritance feature is that 
!    indexes (including unique constraints) and foreign key constraints only 
!    apply to single tables and do not also index their inheritance children.  
!    This is true on both sides of a foreign key constraint.  
!    Thus, in the terms of the above example:
  
     <itemizedlist>
      <listitem>
***************
*** 1236,1244 ****
        Similarly, if we were to specify that
        <structname>cities</>.<structfield>name</> <literal>REFERENCES</> some
        other table, this constraint would not automatically propagate to
!       <structname>capitals</>.  In this case you could work around it by
!       manually adding the same <literal>REFERENCES</> constraint to
!       <structname>capitals</>.
       </para>
      </listitem>
  
--- 1303,1313 ----
        Similarly, if we were to specify that
        <structname>cities</>.<structfield>name</> <literal>REFERENCES</> some
        other table, this constraint would not automatically propagate to
!       <structname>capitals</>.  However, it is possible to set up a 
!       foreign key such as <structname>capitals</>.<structfield>name</> 
!       <literal>REFERENCES</> <structname>states</>.<structfield>name</>. 
!       So it is possible to workaround this restriction by manually adding
!       foreign keys to each child table.
       </para>
      </listitem>
  
***************
*** 1251,1260 ****
--- 1320,1922 ----
      </listitem>
     </itemizedlist>
  
+       Some statements are not optimised in the same way for inherited tables
+       as they are for normal tables. Examples include:
+ 
+    <itemizedlist>
+     <listitem>
+      <para>MIN/MAX aggregates with no GROUP BY
+ <programlisting>
+ SELECT MIN(foo) FROM bar;
+ </programlisting>
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>Retrieving rows from an ordered result set using an index
+ <programlisting>
+ SELECT foo FROM bar ORDER BY foo LIMIT 5;
+ </programlisting>
+      </para>
+     </listitem>
+ 
+    </itemizedlist>
+ 
     These deficiencies will probably be fixed in some future release,
     but in the meantime considerable care is needed in deciding whether
     inheritance is useful for your problem.
+ 
    </para>
+ 
+   <note>
+    <title>Deprecated</title> 
+    <para>
+      In previous versions of <productname>PostgreSQL</productname>, the
+      default behavior was not to include child tables in queries. This was
+      found to be error prone and is also in violation of the SQL:2003
+      standard. Under the old syntax, to get the sub-tables you append
+      <literal>*</literal> to the table name.
+      For example
+ <programlisting>
+ SELECT * from cities*;
+ </programlisting>
+      You can still explicitly specify scanning child tables by appending
+      <literal>*</literal>, as well as explicitly specify not scanning child tables by
+      writing <quote>ONLY</quote>.  But beginning in version 7.1, the default
+      behavior for an undecorated table name is to scan its child tables
+      too, whereas before the default was not to do so.  To get the old
+      default behavior, set the configuration option
+      <literal>SQL_Inheritance</literal> to off, e.g.,
+ <programlisting>
+ SET SQL_Inheritance TO OFF;
+ </programlisting>
+      or add a line in your <filename>postgresql.conf</filename> file.
+    </para>
+   </note>
+ 
+   </sect1>
+ 
+   <sect1 id="ce-partitioning">
+    <title>Constraint Exclusion and Partitioning</title>
+ 
+    <indexterm>
+     <primary>partitioning</primary>
+    </indexterm>
+ 
+    <indexterm>
+     <primary>constraint exclusion</primary>
+    </indexterm>
+ 
+    <para>
+     <productname>PostgreSQL</productname> provides features required
+     to support basic table partitioning. This section describes why and how
+     you can implement this as part of your database design. 
+    </para>
+ 
+    <sect2 id="ce-partitioning-overview">
+      <title>Partitioning Overview</title>
+ 
+    <para>
+     Currently, partitioning is implemented in conjunction with table
+     inheritance only, though using fully SQL:2003 compliant syntax. 
+     Table inheritance gives us the ability to split up a table into partitions
+     and the Constraint Exclusion feature provides the ability to selectively
+     bring partitions back together in response to particular SELECT statements.
+     You should be familiar with Inheritance (see <xref linkend="ddl-inherit">)
+     before attempting to implement partitioning.
+    </para>
+ 
+    <para>
+     Partitioning can provide a number of benefits.
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically for certain kinds
+       of query without the need for maintaining costly indexes.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       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.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Bulk deletes may be avoided altogether by simply removing one of the
+       partitions, if that requirement is planned into the partitioning design.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Seldom used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+ 
+    </itemizedlist>
+ 
+     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.
+     Partitioning can be of use in any type of application. Examples range from
+     data warehousing applications with large historical data stores through
+     to line-of-business applications such as order-entry, CRM or ERP systems.
+    </para>
+ 
+    <para>
+     In &version; the following partitioning types are supported:
+    <itemizedlist>
+     <listitem>
+      <para>
+       "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 (e.g. LogDate) or a range of
+       identifiers for particular business objects (e.g. OrderId).
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       "List Partitioning" where the table is partitioned by explicitly listing
+       which values relate to each partition. 
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Mixed range and list partitioning. No restrictions are placed on the 
+       number or complexity of the partitioning constraints.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Hash partitioning is not currently supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+ 
+     <para>
+      In the next section <xref linkend="ce-partitioning-implementation"> we
+      discuss how to create the DDL required for partitioning. We then move on
+      to a discussion of the Constraint Exclusion feature which allows
+      query performance to be improved, in 
+      <xref linkend="constraint-exclusion-queries">
+     </para>
+ 
+    </sect2>
+ 
+ 
+    <sect2 id="ce-partitioning-implementation">
+      <title>Implementing Partitioning</title>
+ 
+     <para>
+      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.
+     </para>
+  
+     <para>
+      <orderedlist spacing=compact>
+       <listitem>
+        <para>
+         Create the "master" table, from which all of the partitions of that
+         will master will inherit.
+        </para>
+        <para>
+         This table will contain no data.  Do not define any 
+         constraints or keys on this table at this time, unless you intend them
+         to be applied equally to all partitions.
+        </para>
+       </listitem>
+ 
+       <listitem>
+        <para>
+         Create several "child" tables using table inheritance to create
+         identical copies of the master table.
+        </para>
+        <para>
+         We will refer to the child tables as partitions, though they are in
+         every way just normal PostgreSQL data tables. It is advisable to 
+         devise a progressive naming scheme for the partitions.
+        </para>
+       </listitem>
+ 
+       <listitem>
+        <para>
+         Add table constraints to define the allowed values in each partition.
+        </para>
+        <para>
+         Only clauses of the form [COLUMN] [OPERATOR] [CONSTANT(s)] will be used
+         for constraint exclusion. Simple examples would be:
+ <programlisting>
+ CHECK ( x = 1 )
+ CHECK ( county IN ('Oxfordshire','Buckinghamshire','Warwickshire'))
+ CHECK ( outletID BETWEEN 1 AND 99 )
+ </programlisting>
+ 
+         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.
+        </para>
+       </listitem>
+ 
+       <listitem>
+        <para>
+         Add any other indexes you want on the partitions, bearing in mind
+         that it is always more efficient to add them after data has been
+         loaded into the partition.
+        </para>
+       </listitem>
+ 
+       <listitem>
+        <para>
+         (optional) Define a RULE or trigger which tells the database which
+         partition in which to put new data.
+        </para>
+       </listitem>
+ 
+      </orderedlist>
+     </para>
+ 
+ 
+     <para>
+      Now lets look at the process again using a worked example of 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:
+   
+ <programlisting>
+ CREATE TABLE cities (
+     id              int not null,
+     name            text not null,
+     altitude        int     -- (in ft)
+ );
+ 
+ CREATE TABLE measurement (
+     city_id         int not null,
+     logdate         date not null,
+     peaktemp        int,
+     unitsales       int
+ );
+ </programlisting>
+ 
+      As time goes on, sales data is entered. Storing data costs money, 
+      yet older data has less value as ice cream fashions change, so we decide
+      to keep only 3 years data on a rolling 36 months. Each month we remove 
+      the oldest month's data.
+     </para>
+ 
+     <para>
+      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.
+     </para>
+ 
+     <para>
+      In this situation we can use partitioning to help us meet all of our
+      different requirements for the measurements table.
+     </para>
+ 
+     <para>
+      (Step 1) The measurement table is our master table. 
+     </para>
+ 
+     <para>
+      (Step 2) Next we create 36 month-size partitions, using the PostgreSQL
+      inheritance feature like this:
+     
+ <programlisting>
+ 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);
+ </programlisting>
+ 
+      Each of the tables are complete tables in their own right, but they 
+      inherit their definition from the measurement table.
+     </para>
+ 
+     <para>
+      This solves one of our problems: deleting older 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. 
+     </para>
+ 
+     <para>
+      (Step 3) We now add non-overlapping table constraints, so that our table
+      creation script becomes:
+ 
+  <programlisting>
+ 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);
+ </programlisting>
+ 
+      Note that we use constants in the CHECK constraints rather than a 
+      calculation involving INTERVALS. 
+     </para>
+ 
+     <para>
+      (Step 4) We choose not to add further indices at this time.
+     </para>
+ 
+     <para>
+      (Step 5) 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.
+ 
+ <programlisting>
+ 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 );
+ </programlisting>
+ 
+     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.
+ 
+ <programlisting>
+ 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 );
+ </programlisting>
+ 
+      Note that the WHERE clauses in each RULE exactly match those used
+      for the CHECK constraints on each partition. 
+     </para>
+ 
+     <para>
+      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 would be advisable to create a script/program that 
+      generates the SQL DDL required automatically rather than manually
+      maintaining all of the required code.
+     </para>
+ 
+    <para>
+     The following caveats apply:
+    <itemizedlist>
+     <listitem>
+      <para>
+       There is currently no way to specify that all of the CHECK constraints
+       are mutually exclusive. Care is required by the database designer.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       There is currently no way to specify that rows may not be inserted
+       into the master table. Any CHECK constraint on the master
+       table will be inherited by all child tables, so could not be used for
+       that purpose.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       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.
+ <programlisting>
+ CHECK ( x = 1)
+ </programlisting>
+       For BIGINT we must use a constraint like:
+ <programlisting>
+ CHECK ( x = 1::bigint)
+ </programlisting>
+       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.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       There is currently no way to specify that rows may not be inserted
+       into the master table. If you define a CHECK constraint on the master
+       table, then this constraint will be inherited by all child tables. All
+       queries will then return no rows.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       Partitioning can also be arranged using a UNION ALL view, such as the 
+       following view. 
+ 
+ <programlisting>
+ 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;
+ </programlisting>
+ 
+       However, in this release this means of specifying a
+       partitioned table does not support Constraint Exclusion and is not
+       recommended.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+ 
+ 
+    </sect2>
+ 
+    <sect2 id="constraint-exclusion-queries">
+      <title>Constraint Exclusion in Queries</title>
+ 
+    <para>
+     Partitioning can be used to improve query performance when used in 
+     conjunction with the Constraint Exclusion (CE) feature. As an example:
+ 
+ <programlisting>
+ SET constraint_exclusion=true;
+ SELECT count(*) FROM measurement WHERE logdate >= DATE '2006-01-01';
+ </programlisting>
+ 
+     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. 
+    </para>
+ 
+    <para>
+     You can use the EXPLAIN command to show the difference
+     between a plan with constraint_exclusion on and a plan with it off.
+ 
+ <programlisting>
+ 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)
+ </programlisting>
+ 
+     Now when we enable CE, we get a significantly reduced plan, yet one that
+     returns exactly the same answer (provably).
+ 
+ <programlisting>
+ 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)
+ </programlisting>
+ 
+     Don't forget that you still need to run ANALYZE on each partition
+     individually. A simple command like this
+ <programlisting>
+ ANALYZE measurement;
+ </programlisting>
+ 
+     does not ANALYZE each of the partitions, only the master table.
+    </para>
+ 
+    <para>
+     No indexes are required to make CE function. The partitions should be 
+     defined with appropriate CHECK constraints
+     and these are able to be compared against clauses on the query.
+    </para>
+ 
+    <para>
+     The following caveats apply to this release:
+    <itemizedlist>
+     <listitem>
+      <para>
+       CE 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.
+       Joins conditions will not allow CE to work either.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       UPDATEs and DELETEs against the partitioning master table do 
+       not perform constraint exclusion. One of the purposes of 
+       partitioning is to avoid bulk DELETE statements so you are advised
+       to rethink your design if you attempting this.
+      </para>
+     </listitem>
+ 
+     <listitem>
+      <para>
+       All constraints on all partitions of the master table are considered for
+       Constraint Exclusion, so large numbers of partitions are likely to 
+       increase query parse time considerably.
+      </para>
+     </listitem>
+ 
+    </itemizedlist>
+    </para>
+ 
+    </sect2>
+ 
   </sect1>
  
   <sect1 id="ddl-alter">
---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?

               http://www.postgresql.org/docs/faq

Reply via email to