我正在使用"插入...选择...存在..."批量插入行到表中,同时忽略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
第二节:
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".
我希望该行被过滤掉,因此不会被插入。在我的实际场景中,这是一个批量插入,所以理想情况下我想避免重试。
提高隔离级别并没有帮助。
答案 0 :(得分:2)
您可以更改会话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
并处理ROLLBACK TO SAVEPOINT
的错误以避免重试整个事务