实际上,您创建了一个规则相互矛盾的竞争条件。
我的第一个冲动是检查DEFERRED
约束是否有帮助。但它没有任何区别是有道理的。
我发现脚本中首先出现CREATE TABLE
的 FK 约束是这场比赛的获胜者。如果ON DELETE CASCADE
先到,则级联删除,如果ON DELETE RESTRICT
先到,则中止操作。
考虑SQL Fiddle上的演示。
这似乎与oid
目录表中的较小值相关pg_constraint
:
SELECT oid, * FROM pg_constraint WHERE conrelid = 'task'::regclass
但是您的反馈表明,这不是原因。也许pg_attribute.attnum
决定比赛。无论哪种方式,只要它没有记录在案的行为,您就不能依靠它在下一个主要版本中保持这种状态。可能值得在 pgsql-general@postgresql.org 上发布一个问题。
与所有这些无关,您需要考虑其他行:即使CASCADE
将通过task
同时具有tenant_id
并customer_id
指向a 的行person
,如果任何行仅customer_id
引用,它仍然会受到限制person
。
另一个演示此案例的SQL Fiddle 。
如何禁用约束?
你最好的选择是放弃并重新创建它。在事务中执行所有操作,以确保您不会破坏参照完整性。
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;
这会独占锁定表,不适合在多用户环境中进行日常使用。
我怎么知道约束的名称?我从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;
更多的手册在这里。但这会禁用所有触发器。我没有运气尝试仅禁用系统创建的触发器来实现单个 FK 约束。
其他替代方法是使用触发器或规则来实施您的制度。这可以正常工作,但这些执行不如外键严格。