由于引用表

时间:2018-03-21 19:47:32

标签: postgresql

我正在使用"插入...选择...存在..."批量插入行到表中,同时忽略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".

我希望该行被过滤掉,因此不会被插入。在我的实际场景中,这是一个批量插入,所以理想情况下我想避免重试。

提高隔离级别并没有帮助。

1 个答案:

答案 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的错误以避免重试整个事务