我在用“插入。。。选择。。。在……“将批处理插入到表中,而忽略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回滚事务)。
如果您不介意等待锁,并且不想错过这些角落案例,那么您必须使用
SAVEPOINT
s并使用ROLLBACK TO SAVEPOINT
处理错误,以避免重试整个事务。关于postgresql - 忽略由于并发删除引用表而导致的插入错误,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49415327/