我在用“插入。。。选择。。。在……“将批处理插入到表中,而忽略FK约束无效的行”。但是,如果有一个并发事务刚刚删除了被引用表中的一行,则此操作不起作用。
考虑以下psql会话:
会议1:

coudy=# create table a (x int primary key);
CREATE TABLE
coudy=# create table b (x int, foreign key (x) references a);
CREATE TABLE
coudy=# insert into a values (1);
INSERT 0 1
coudy=# begin;
BEGIN
coudy=# delete from a where x = 1;
DELETE 1

会议2:
coudy=# begin;
BEGIN
coudy=# insert into b select v.x from (values (1)) v (x) where exists (select 1 from a where a.x = v.x);

这正确地阻塞了会话2。但是,在会话1中提交之后,会话2现在意外失败:
ERROR:  insert or update on table "b" violates foreign key constraint "b_x_fkey"
DETAIL:  Key (x)=(1) is not present in table "a".

我本以为这行会被过滤掉而不会被插入。在我的实际场景中,这是一个批插入,因此理想情况下,我希望避免重试。
提高隔离级别没有帮助。

最佳答案

您可以将会话2更改为执行以下操作:

INSERT INTO b
   SELECT x FROM (VALUES (1)) v(x)
   WHERE EXISTS (SELECT 1 FROM a
                 WHERE a.x = v.x
                 FOR SHARE SKIP LOCKED);

内部的SELECT将跳过持有排他锁的所有行,因为它们正在被更新或删除。
这不会阻塞或出错,但可能无法插入一些本来可以正常工作的行(假设会话1回滚事务)。
如果您不介意等待锁,并且不想错过这些角落案例,那么您必须使用SAVEPOINTs并使用ROLLBACK TO SAVEPOINT处理错误,以避免重试整个事务。

关于postgresql - 忽略由于并发删除引用表而导致的插入错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49415327/

10-11 22:37
查看更多