是否可以防止删除而不是插入外键?
Is it possible to prevent delete but not an insert with foreign key?
我有两个表,我想 link 使用 single-column 外键。正常的 parent-child 类型的东西。我要解决的问题是系统的性质是 child 可以在 parent 存在之前到达(child 知道 parent 会)。
我不想延迟 child 的插入等待 parent 到达,速度很重要。但我确实想要一些参照完整性 - 如果 child 存在,我不想删除 parent。
我假设我无法使用 Postgresql 中的常规外键实现此目的?是写某种触发器的解决方案吗?
您可以使用 "deferrable" 约束来执行此操作。
create table child (parent_id integer references parent deferrable initially immediate);
然后您可以在事务中使用 set constraints
来临时延迟约束,如下所示:
start transaction;
set constraints all deferred;
-- Your inserts go here --
commit;
如果您在同一个数据库事务中创建 parent 和 child 并且您无法控制插入顺序,那么正确的方法是延迟约束,正如@Ragesh 所建议的那样。
否则,您猜对了:解决方案是编写某种触发器。
Postgres 中的外键约束只不过是一对触发器,具体而言:
- 在 child 的
INSERT
(或 FK 的 UPDATE
)上触发,以检查 parent 是否存在,并获取 KEY SHARE
锁定以确保 parent 不会在 COMMIT
之前消失
- 触发 parent 的
DELETE
(或 PK 的 UPDATE
)以检查它是否没有 children
在这种情况下,您只需要 parent 的触发器,而不需要 child 的触发器。幸运的是,自己编写非常简单。应该这样做:
create table parent (id int primary key);
create table child (id int primary key, parent_id int);
create function trg_parent_fk_check() returns trigger language plpgsql as $$
begin
if exists(select 1 from child where parent_id = old.id) then
raise foreign_key_violation using
message = 'parent.id=' || old.id || ' is still referenced by child.parent_id';
end if;
end
$$;
create trigger trg_parent_fk_check_del
after delete on parent
for each row
execute function trg_parent_fk_check();
/* You probably shouldn't be modifying primary keys, but just in case... */
create trigger trg_parent_fk_check_upd
after update of id on parent
for each row
when (old.id <> new.id)
execute function trg_parent_fk_check();
当然,这种方法允许 child 到达的可能性,但 parent 没有。您可能希望包括一些预定进程来查找和删除孤立记录。
我有两个表,我想 link 使用 single-column 外键。正常的 parent-child 类型的东西。我要解决的问题是系统的性质是 child 可以在 parent 存在之前到达(child 知道 parent 会)。
我不想延迟 child 的插入等待 parent 到达,速度很重要。但我确实想要一些参照完整性 - 如果 child 存在,我不想删除 parent。
我假设我无法使用 Postgresql 中的常规外键实现此目的?是写某种触发器的解决方案吗?
您可以使用 "deferrable" 约束来执行此操作。
create table child (parent_id integer references parent deferrable initially immediate);
然后您可以在事务中使用 set constraints
来临时延迟约束,如下所示:
start transaction;
set constraints all deferred;
-- Your inserts go here --
commit;
如果您在同一个数据库事务中创建 parent 和 child 并且您无法控制插入顺序,那么正确的方法是延迟约束,正如@Ragesh 所建议的那样。
否则,您猜对了:解决方案是编写某种触发器。
Postgres 中的外键约束只不过是一对触发器,具体而言:
- 在 child 的
INSERT
(或 FK 的UPDATE
)上触发,以检查 parent 是否存在,并获取KEY SHARE
锁定以确保 parent 不会在COMMIT
之前消失
- 触发 parent 的
DELETE
(或 PK 的UPDATE
)以检查它是否没有 children
在这种情况下,您只需要 parent 的触发器,而不需要 child 的触发器。幸运的是,自己编写非常简单。应该这样做:
create table parent (id int primary key);
create table child (id int primary key, parent_id int);
create function trg_parent_fk_check() returns trigger language plpgsql as $$
begin
if exists(select 1 from child where parent_id = old.id) then
raise foreign_key_violation using
message = 'parent.id=' || old.id || ' is still referenced by child.parent_id';
end if;
end
$$;
create trigger trg_parent_fk_check_del
after delete on parent
for each row
execute function trg_parent_fk_check();
/* You probably shouldn't be modifying primary keys, but just in case... */
create trigger trg_parent_fk_check_upd
after update of id on parent
for each row
when (old.id <> new.id)
execute function trg_parent_fk_check();
当然,这种方法允许 child 到达的可能性,但 parent 没有。您可能希望包括一些预定进程来查找和删除孤立记录。