【发布时间】:2017-10-05 13:24:36
【问题描述】:
使用 Postgres 9.6,我按照https://stackoverflow.com/a/40325406/435563 中推荐的策略执行INSERT 或SELECT 并返回结果id:
with ins as (
insert into prop (prop_type, norm, hash, symbols)
values (
$1, $2, $3, $4
) on conflict (hash) do
update set prop_type = 'jargon' where false
returning id)
select id from ins
union all
select id from prop where hash = $3
但是,有时这不会返回任何内容。无论如何,我都希望它会返回一行。如何修复它以确保它始终返回一个 id?
注意,尽管没有返回一行,但在检查时该行似乎确实存在。我认为问题可能与尝试同时通过两个会话添加相同的记录有关。
有问题的表定义为:
create table prop (
id serial primary key,
prop_type text not null references prop_type(name),
norm text not null,
hash text not null unique,
symbols jsonb
);
数据:
EDT DETAIL: parameters: $1 = 'jargon', $2 = 'j2', $3 = 'lXWkZSmoSE0mZ+n4xpWB', $4 = '[]'
如果我将prop_type = 'jargon' 更改为prop_type = 'foo',它会起作用!如果即使给定where false 子句,表达式也不会改变任何内容,似乎不会使用锁定。不过,这真的需要依赖于我猜测一个不在行中的值吗?还是有更好的方法来确保您获得锁定?
--- 更新 ---
总体情况是应用程序尝试使用连接池(...使用自动提交)保存有向无环图,并在筛选出重复项的同时使用此查询获取 id。 [事实证明,更聪明的是使用事务并仅序列化到一个连接。但是这里发生争用时的行为很奇怪。]
外键约束似乎不会影响插入——例如:
create table foo(i int unique, prop_id int references prop(id));
insert into foo values (1, 208);
insert into foo values (1, 208)
on conflict (i) do update set prop_id = 208 where false;
--> INSERT 0 0
insert into foo values (1, 208)
on conflict (i) do update set prop_id = -208 where false;
--> INSERT 0 0
注意一个具有有效的 fk 208,另一个具有无效的 -208。如果我使用完整模式将选择连接到其中任何一个上,那么在没有争用的情况下,它们都会按预期返回 i = 1。
【问题讨论】:
-
请同时发布数据样本,如果我没记错,它总是返回一行,因此您甚至可以从 xmin,xmax 判断它是否已更新或插入
-
添加数据。我认为它与尝试使用连接池保存此叶子出现两次的复杂数据结构的应用程序有关。所以记录被添加到两个不同的连接中。
-
随着您的更新,我注意到 false 导致更新不会发生的地方 - 答案会有所不同:)
-
我参考的答案是关于
SELECT或INSERT,而不是INSERT或UPDATE。在第一行以粗体表示。这个问题也是如此。我建议你更新第一句话。
标签: sql postgresql concurrency upsert sql-returning