我应该暂时禁用外键约束吗?怎么样?

时间:2013-02-21 15:57:39

标签: sql database postgresql foreign-keys

我有两张桌子:

person:
    id serial primary key,
    name varchar(64) not null

task:
    tenant_id   integer not null references person (id) on delete cascade,
    customer_id integer not null references person (id) on delete restrict

(他们有比这更多的列,但其余的与问题无关。)

问题是,我想在删除租户task时级联删除person。但是当租户和客户是同一个人时,customer_id外键约束将限制删除。

我的问题分为两部分:

  1. 暂时禁用第二个外键是我唯一的选择吗?
  2. 如果是这样,那么我如何在PostgreSQL中做到这一点?

1 个答案:

答案 0 :(得分:12)

有效地创建了具有矛盾规则的竞争条件

我的第一个冲动是检查DEFERRED约束是否有帮助。但它有意义没有任何区别。

我发现CREATE TABLE 脚本中首先出现的FK约束是此种族的赢家。如果ON DELETE CASCADE首先出现,则删除是级联的,如果ON DELETE RESTRICT出现,则操作将中止。

考虑SQL Fiddle上的演示。

这似乎与目录表pg_constraint中较小的oid相关联:

SELECT oid, * FROM pg_constraint WHERE conrelid = 'task'::regclass

但是你的反馈表明,这不是原因。也许pg_attribute.attnum决定比赛。无论哪种方式,只要它没有记录的行为,你就不能依赖它在下一个主要版本中保持这种状态。可能值得在pgsql-general@postgresql.org上发布一个问题。

独立于所有这些,您需要考虑其他行:即使CASCADEtask中同时指向tenant_idcustomer_id的行,也会考虑person customer_id,如果任何行只有person引用BEGIN; ALTER TABLE task DROP CONSTRAINT task_customer_id_fkey; DELETE FROM person WHERE id = 3; ALTER TABLE task ADD CONSTRAINT task_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES person (id) ON DELETE RESTRICT; COMMIT; ,它仍会受到限制。
另一个SQL Fiddle证明了这一情况。

如何禁用约束?

最好的办法是放弃并重新创建它。在事务中完成所有操作以确保不会破坏引用完整性。

pg_constraint

这会独占锁定表,不适合在多用户环境中常规使用。

我如何知道约束的名称?如上所示,我从CREATE TEMP TABLE task ( customer_id integer NOT NULL ,tenant_id integer NOT NULL REFERENCES person (id) ON DELETE CASCADE ,CONSTRAINT task_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES person (id) ON DELETE RESTRICT ); 获取了它。可能更容易使用显式约束名称开头:

ALTER TABLE task DISABLE trigger ALL;

还有

{{1}}

More in the manual here。但这会禁用所有触发器。我没有运气试图仅禁用系统创建的触发器来实现单个FK约束。

其他替代方案是使用triggersrules来实施您的制度。这样可以正常工作,但这些并不像外键那样严格执行。