punkish
punkish

Reputation: 15248

How do I INSERT and SELECT data with partitioned tables?

I set up a set of partitioned tables per the docs at http://www.postgresql.org/docs/8.1/interactive/ddl-partitioning.html

CREATE TABLE t (year, a);
CREATE TABLE t_1980 ( CHECK (year = 1980) ) INHERITS (t);
CREATE TABLE t_1981 ( CHECK (year = 1981) ) INHERITS (t);
CREATE RULE t_ins_1980 AS ON INSERT TO t WHERE (year = 1980)
    DO INSTEAD INSERT INTO t_1980 VALUES (NEW.year, NEW.a);
CREATE RULE t_ins_1981 AS ON INSERT TO t WHERE (year = 1981)
    DO INSTEAD INSERT INTO t_1981 VALUES (NEW.year, NEW.a);

From my understanding, if I INSERT INTO t (year, a) VALUES (1980, 5), it will go to t_1980, and if I INSERT INTO t (year, a) VALUES (1981, 3), it will go to t_1981. But, my understanding seems to be incorrect. First, I can't understand the following from the docs

"There is currently no simple way to specify that rows must not be inserted into the master table. A CHECK (false) constraint on the master table would be inherited by all child tables, so that cannot be used for this purpose. One possibility is to set up an ON INSERT trigger on the master table that always raises an error. (Alternatively, such a trigger could be used to redirect the data into the proper child table, instead of using a set of rules as suggested above.)"

Does the above mean that in spite of setting up the CHECK constraints and the RULEs, I also have to create TRIGGERs on the master table so that the INSERTs go to the correct tables? If that were the case, what would be the point of the db supporting partitioning? I could just set up the separate tables myself? I inserted a bunch of values into the master table, and those rows are still in the master table, not in the inherited tables.

Second question. When retrieving the rows, do I select from the master table, or do I have to select from the individual tables as needed? How would the following work?

SELECT year, a FROM t WHERE year IN (1980, 1981);

Update: Seems like I have found the answer to my own question

"Be aware that the COPY command ignores rules. If you are using COPY to insert data, you must copy the data into the correct child table rather than into the parent. COPY does fire triggers, so you can use it normally if you create partitioned tables using the trigger approach."

I was indeed using COPY FROM to load data, so RULEs were being ignored. Will try with TRIGGERs.

Upvotes: 5

Views: 16252

Answers (3)

Zeljan Rikalo
Zeljan Rikalo

Reputation: 11

Triggers are definitelly better than rules. Today I've played with partitioning of materialized view table and run into problem with triggers solution. Why ? I'm using RETURNING and current solution returns NULL :) But here's solution which works for me - correct me if I'm wrong. 1. I have 3 tables which are inserted with some data, there's an view (let we call it viewfoo) which contains data which need to be materialized. 2. Insert into last table have trigger which inserts into materialized view table via INSERT INTO matviewtable SELECT * FROM viewfoo WHERE recno=NEW.recno; That works fine and I'm using RETURNING recno; (recno is SERIAL type - sequence).

Materialized view (table) need to be partitioned because it's huge, and according to my tests it's at least x10 faster for SELECT in this case. Problems with partitioning: * Current trigger solution RETURN NULL - so I cannot use RETURNING recno. (Current trigger solution = trigger explained at depesz page).

Solution: I've changed trigger of my 3rd table TO NOT insert into materialized view table (that table is parent of partitioned tables), but created new trigger which inserts partitioned table directly FROM 3rd table and that trigger RETURN NEW. Materialized view table is automagically updated and RETURNING recno works fine. I'll be glad if this helped to anybody.

Upvotes: 1

rfusca
rfusca

Reputation: 7705

Definitely try triggers.

If you think you want to implement a rule, don't (the only exception that comes to mind is updatable views). See this great article by depesz for more explanation there.

In reality, Postgres only supports partitioning on the reading side of things. You're going to have setup the method of insertition into partitions yourself - in most cases TRIGGERing. Depending on the needs and applicaitons, it can sometimes be faster to teach your application to insert directly into the partitions.

When selecting from partioned tables, you can indeed just SELECT ... WHERE... on the master table so long as your CHECK constraints are properly setup (they are in your example) and the constraint_exclusion parameter is set corectly.

For 8.4:

SET constraint_exclusion = partition;

For < 8.4:

SET constraint_exclusion = on;

All this being said, I actually really like the way Postgres does it and use it myself often.

Upvotes: 4

leonbloy
leonbloy

Reputation: 75936

Does the above mean that in spite of setting up the CHECK constraints and the RULEs, I also have to create TRIGGERs on the master table so that the INSERTs go to the correct tables?

Yes. Read point 5 (section 5.9.2)

If that were the case, what would be the point of the db supporting partitioning? I could just set up the separate tables myself?

Basically: the INSERTS in the child tables must be done explicitly (either creating TRIGGERS, or by specifying the correct child table in the query). But the partitioning is transparent for SELECTS, and (given the storage and indexing advantages of this schema) that's the point. (Besides, because the partitioned tables are inherited, the schema is inherited from the parent, hence consistency is enforced).

Upvotes: 1

Related Questions