我喜欢
之类的东西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),则抛出错误。
有更好的方法吗?
答案 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
那样)。您仍然需要处理这些错误。