我有两张桌子:
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
外键约束将限制删除。
我的问题分为两部分:
答案 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上发布一个问题。
独立于所有这些,您需要考虑其他行:即使CASCADE
在task
中同时指向tenant_id
和customer_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约束。