[英]Should I temporarily disable foreign key constraints? How?
I have two tables: 我有两张桌子:
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
(They have a lot more columns than that, but the rest aren't relevant to the question.) (他们有比这更多的列,但其余的与问题无关。)
The problem is, I want to cascade-delete a task
when its tenant person
is deleted. 问题是,我想级联删除
task
时,其承租人person
将被删除。 But when the tenant and the customer are the same person, the customer_id
foreign key constraint will restrict deletion. 但是当租户和客户是同一个人时,
customer_id
外键约束将限制删除。
My question has two parts: 我的问题有两个部分:
Effectively you create a race condition with contradicting rules. 实际上你创造了一个矛盾规则的竞争条件 。
My first impulse was to check whether a DEFERRED
constraint would help. 我的第一个冲动是检查
DEFERRED
约束是否有帮助。 But it makes sense that it doesn't make any difference. 但它没有任何区别是有道理的。
I found that the FK constraint that comes first in the CREATE TABLE
script is the winner of this race. 我发现在
CREATE TABLE
脚本中首先出现的FK约束是这场比赛的胜利者。 If the ON DELETE CASCADE
comes first, the delete is cascaded, if ON DELETE RESTRICT
comes first, the operation is aborted. 如果
ON DELETE CASCADE
首先出现,则删除是级联的,如果ON DELETE RESTRICT
先出现,则操作中止。
Consider the demo on SQL Fiddle . 考虑一下SQL Fiddle的演示。
This seems to correlate with a smaller oid
in the catalog table pg_constraint
: 这似乎与目录表
pg_constraint
较小的oid
相关:
SELECT oid, * FROM pg_constraint WHERE conrelid = 'task'::regclass
But your feedback indicates, this is not the cause. 但是你的反馈表明,这不是原因。 Maybe
pg_attribute.attnum
decides the race. 也许
pg_attribute.attnum
决定比赛。 Either way, as long as it is not documented behavior you cannot rely on it to stay that way in the next major version. 无论哪种方式,只要它没有记录的行为,你就不能依赖它在下一个主要版本中保持这种状态。 Might be worth to post a question on pgsql-general@postgresql.org.
可能值得在pgsql-general@postgresql.org上发布一个问题。
Independent from all that, you need to consider other rows: even if CASCADE
would go through for a row in task
that has both tenant_id
and customer_id
pointing to a person
, it will still be restricted if any row has only customer_id
referencing person
. 独立于所有这些,你需要考虑其他行:即使
CASCADE
在一个同时具有tenant_id
和customer_id
指向某person
task
中的行中,如果任何行只有customer_id
引用person
,它仍将受到限制。
Another SQL Fiddle demonstrating the case. 另一个SQL小提琴演示案例。
Your best bet is to drop and recreate it. 你最好的选择是放弃并重新创建它。 Do it all inside a transaction to make sure you don't corrupt referential integrity.
在事务中完成所有操作以确保您不会破坏参照完整性。
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;
This locks the table exclusively and is not fit for routine use in a multi-user environment. 这将独占锁定表,不适合在多用户环境中进行常规使用。
How did I know the name of the constraint? 我怎么知道约束的名字 ? I took it from
pg_constraint
as demonstrated above. 我从
pg_constraint
演示的pg_constraint
了它。 Might be easier to use an explicit constraint name to begin with: 可能更容易使用显式约束名称开头:
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
);
There is also 还有
ALTER TABLE task DISABLE trigger ALL;
More in the manual here . 更多在手册中 。 But that would disable all triggers.
但这会禁用所有触发器。 I had no luck trying to disable only the trigger created by the system to implement a single FK constraint.
我没有运气试图仅禁用系统创建的触发器来实现单个FK约束。
Other alternatives would be to implement your regime with triggers or rules . 其他替代方案是通过触发器或规则实施您的制度。 That would work just fine, but those are not enforced as strictly as foreign keys.
这样可以正常工作,但这些并不像外键那样严格执行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.