使用postrgres进行幂等插入

时间:2017-11-23 10:00:19

标签: sql postgresql

我喜欢

之类的东西
INSERT VALUES(1,2,3) INTO sometable ON CONFLICT DO NOTHING IF EXACTLY SAME ROW

所以我喜欢以下行为:

#CREATE TABLE sometable (a int primary key, b int, c int);

CREATE TABLE
 #INSERT INTO sometable (1,2,3) ON CONFLICT DO NOTHING IF EXACTLY SAME ROW
INSERT 0 1
 #INSERT INTO sometable (1,2,3) ON CONFLICT DO NOTHING IF EXACTLY SAME ROW
INSERT 0 0
 #INSERT INTO sometable (1,3,2) ON CONFLICT DO NOTHING IF EXACTLY SAME ROW
ERROR:  duplicate key value violates unique constraint "sometable_pkey"
DETAIL:  Key (a)=(1) already exists.

希望这看起来很自然,因为客户端应用程序无法假设它会知道插入是否成功(如果postgres或客户端崩溃或网络出现故障,请求可能已被处理但客户端从未收到确认)。因此,任何编写良好的应用程序都需要以某种方式处理此案例。

但是,我发现实现这一目标的最不好的方法仍然非常烦人:

INSERT INTO sometable (a,b,c) VALUES(1,2,3) ON CONFLICT(a) UPDATE set sometable.b=2 WHERE sometable.b=2 AND sometable.c=3;

换句话说,执行无操作更新,但仅当值是您要插入的值时,如果触摸了0行(而不是1),则抛出错误。

有更好的方法吗?

1 个答案:

答案 0 :(得分:3)

您可以使用基于选择的INSERT:

insert into sometable
select *
from ( values (1,2,3) ) as data(a,b,c)
where not exists (select *
                  from sometable
                  where data = sometable);

是的,条件where data = sometable在Postgres中有效,只是比较所有列。

这也可以扩展到多行:

insert into sometable
select *
from ( 
  values 
    (1,2,3),
    (4,5,6),
    (7,8,9)
) as data(a,b,c)
where not exists (select *
                  from sometable
                  where data = sometable);

如果从多个事务中完成,这不会阻止PK违规错误(如on conflict那样)。您仍然需要处理这些错误。