为什么postgreSQL会两次检查外键约束?

时间:2010-09-15 09:05:54

标签: database postgresql foreign-keys

我有一个带有两个表的postgresql数据库,一个具有引用另一个的外键约束。约束设置为在更新和删除时级联,因此引用表上的更新应级联到引用表。当我分析引用表的更新时,使用explain analyze我得到了以下输出:

...
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
...

我假设第二个触发器被调用40次,因为这是此关系中更新值出现的次数。

我的问题是为什么需要检查两个表的约束?此外,为什么检查引用表的触发器比引用表花费的时间长得多,因为与引用表触发器(称为40次)相比,它只被调用一次。如果有人能够了解到究竟发生了什么,或者为什么需要这么长时间,我们将非常感激。

这是架构:

CREATE TABLE课程 ( course_id BIGINT CONSTRAINT course_pk PRIMARY KEY, 标题CHAR(40) );

CREATE TABLE讲师 ( 讲师_id BIGINT CONSTRAINT讲座_pk PRIMARY KEY, lec_name CHAR(40) );

CREATE TABLE教授 ( lecturer_id BIGINT CONSTRAINT teaches_lecturer_fk1 REFERENCES讲师(讲师_id), course_id BIGINT CONSTRAINT teaches_course_fk1 REFERENCES课程(course_id), desc_teaches CHAR(40), CONSTRAINT teaches_pk PRIMARY KEY(lecturer_id,course_id) );

查询和输出:

解释分析UPDATE讲师SET讲师_id = 301 WHERE讲话者= 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

提前致谢。

3 个答案:

答案 0 :(得分:2)

(通过复制你的例子,使用PostgreSQL 8.4.4,检查pg_triggerpg_proc表以及最新的源代码来解决问题。)

触发器运行两次:一次在主语句中更新lecturer时;然后再次更新每个teaches行以引用lecturer中的新值。

约束是作为PostgreSQL触发器实现的。执行UPDATE语句时,PG会识别UPDATE lecturer上的触发器。然后它执行相应的触发器函数PostgreSQL内部函数RI_FKey_cascade_upd。这相当于解释输出中的第一个Trigger事件:

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

执行一些检查后,RI_FKey_cascade_upd会为UPDATE生成teaches语句,以更新lecturer中新值的外键。由于此表上还有UPDATE触发器,因此使用内部函数RI_FKey_check_upd。此函数检查新值是否与PK表中的相应行匹配。由于级联更新,FK正在改变每一行调用触发器。这解释了解释输出中的第二个事件:

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

据推测,teaches中共有40行受到级联更新。

我不确定每个触发器的成本来自何处。我一开始认为级联触发器的成本将包含在主要成本中,但teaches中有10000个受影响行的测试不支持这一点:

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

但是我运行的版本与最新的代码不同,所以自8.4.4以来可能会有一个优化RI_FKey_cascade_upd的更改。或者,同样可能,我没有正确阅读代码......

答案 1 :(得分:0)

每行都有自己的支票,40次更新,40次检查。这是一个基于行的触发器。您在两个不同的表中看到两个触发器,这必须是因为不同的检查。您能告诉我们您的更新中涉及的数据库架构吗?还有您的更新查询,这可能有助于了解您的数据库中发生了什么。

your_database<> MY_DATABASE

看看pg_trigger,它会显示FK触发器。

答案 2 :(得分:0)

设置外键不会设置索引以快速进行检查,您必须单独执行此操作。如果没有索引,主表上的删除将导致外表上的表扫描。

此外,可能会无意中多次创建相同的约束,导致postgres多次执行检查。