是否可以防止删除而不是插入外键?

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 没有。您可能希望包括一些预定进程来查找和删除孤立记录。