views:

114

answers:

3

I have a postgresql database with two tables, one which has a foreign key constraint which references the other. The constraint is set to cascade on update and delete, so an update on the referenced table should cascade to the referencing table. When I analyzed an update on the referenced table, using explain analyze the I got the following output:

...
Trigger for constraint foreign_key_constraint on referencedtable: time=33.840 calls=1
Trigger for constraint foreign_key_constraint on referencingtable: time=2.394 calls=40
...

I assume the second trigger is called 40 times as this is the amount of times the updated value appeared in this relation.

My question is why does the constraint need to be checked for both tables? Also, why does the trigger for checking the referenced table take so much longer then the referencing table, as it is called only once compared to the referencing table trigger which is called 40 times. If anyone can shed some light as to what exactly is happening or why it takes so long that would be greatly appreciated.

Here is the schema:

CREATE TABLE course ( course_id BIGINT CONSTRAINT course_pk PRIMARY KEY, title CHAR(40) );

CREATE TABLE lecturer ( lecturer_id BIGINT CONSTRAINT lecturer_pk PRIMARY KEY, lec_name CHAR(40) );

CREATE TABLE teaches ( lecturer_id BIGINT CONSTRAINT teaches_lecturer_fk1 REFERENCES lecturer (lecturer_id), course_id BIGINT CONSTRAINT teaches_course_fk1 REFERENCES course (course_id), desc_teaches CHAR(40), CONSTRAINT teaches_pk PRIMARY KEY (lecturer_id, course_id) );

Query and output:

explain analyze UPDATE lecturer SET lecturer_id = 301 WHERE lecturer_id = 57;

Trigger for constraint teaches_lecturer_fk1 on lecturer: time=33.840 calls=1
Trigger for constraint teaches_lecturer_fk1 on teaches: time=2.394 calls=40

Thanks in advance.

A: 

Every row has it's own check, 40 updates, 40 checks. It's a row based trigger. You see two triggers in two different tables, that must be because of different checks. Could you show us the database schema involved in your update? And your update query as well, that might help to understand what is going on in your database.

your_database <> my_database

Take a look at pg_trigger, it will show you the FK-triggers.

Frank Heikens
I only have the single foreign key constraint between these two tables. It appears as though the implicit trigger created when the foreign key constraint is created is being called twice, once for the referenced table, and 40 times for the referencing table. I was just wondering why this is, and why the single call on the referenced table takes so much longer than 40 calls on the much larger referencing table. I'll add the schema and query to my original post.
Strange, I only see one table used in the constraint check. What PostgreSQL version do you use?
Frank Heikens
A: 

Setting a foreign key does not also set an index to make the check fast, you have to do that separately. Without the index, a delete on the master table will cause a table-scan on the foreign table.

Also, it is possible to inadvertently create the same constraint multiple times which causes postgres to perform the check multiple times.

Adrian Pronk
Sorry, I did not make the size of the tables clear. The referencing table is an order of magnitude larger than then referenced table. I'm confused as to why a single check on a table with ~300 tuples takes so much longer than 40 checks on a table with ~10,000 tuples. As I am updating based on the primary key of the referenced table, I assumed the index will get used when searching the same table.
+1  A: 

(Worked out by reproducing your example, using PostgreSQL 8.4.4, and examining the pg_trigger and pg_proc tables, as well as the latest source.)

Triggers are run twice: once when lecturer is updated in the main statement; then again when each teaches row is updated to refer to the new value in lecturer.

The constraints are implemented as PostgreSQL triggers. When the UPDATE statement is executed, PG recognises that there a trigger on UPDATE of lecturer. It then executes the corresponding trigger function, the PostgreSQL internal function RI_FKey_cascade_upd. This amounts to the first Trigger event in the explain output:

Trigger for constraint teaches_lecturer_fk1 on lecturer: time=33.840 calls=1

After performing a few checks, RI_FKey_cascade_upd generates an UPDATE statement for teaches, to update the foreign key for the new value in lecturer. As there is also an UPDATE trigger on this table, using the internal function RI_FKey_check_upd. This function checks that the new value matches a corresponding row in the PK table. The trigger is called for each row FK is changing as a result of the cascaded update. This explains the second event in the explain output:

Trigger for constraint teaches_lecturer_fk1 on teaches: time=2.394 calls=40

Presumably there were 40 rows in teaches that were subject to the cascaded update.

I'm not sure where the costs for each trigger come from. I thought at first that the cascaded trigger's costs would be included in the main one, but a test with 10000 affected rows in teaches doesn't support this:

Trigger for constraint teaches_lecturer_fk1 on lecturer: time=540.886 calls=1
Trigger for constraint teaches_lecturer_fk1 on teaches: time=808.930 calls=10000
Total runtime: 1377.017 ms

But then the version I'm running is not the same as the newest code, so perhaps there's been a change since 8.4.4 that optimises RI_FKey_cascade_upd. Or, just as likely, I'm not reading the code properly...

Edmund