一尘不染

INSERT或SELECT策略总是返回一行?

sql

使用Postgres9.6,我遵循了推荐的策略来执行INSERTor或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。如果我将一个select连接到具有完整模式的任何一个上,那么在无争用的情况下,它们都将按预期返回i = 1。


阅读 147

收藏
2021-03-17

共1个答案

一尘不染

您的观察 似乎 不可能。上面的命令应 始终
为新插入的行或先前存在的行返回一个ID。并发写入不会对此造成混乱,因为现有的冲突行已被锁定。相关答案中的解释:

__当然, 除非 引发异常
。在这种情况下,您会收到一条错误消息,而不是结果。你检查了吗?您是否有错误处理?(以防您的应用以某种方式丢弃错误消息:1)修复此问题。2)数据库日志中还有一个附加条目,具有默认的日志记录设置。)

我确实在您的表定义中看到了FK约束:

prop_type text not null references prop_type(name),

如果您尝试插入违反约束的行,则确实会发生这种情况。如果name = 'jargon'table中没有任何行,prop_type那么您将得到:

ERROR:  insert or update on table "prop" violates foreign key constraint

“prop_prop_type_fkey”
DETAIL: Key (prop_type)=(jargon) is not present in table “prop_type”.

演示:

dbfiddle
在这里

您的观察将符合以下罪行:

如果我将prop_type =’jargon’更改为prop_type =’foo’,它将起作用!

但是您的解释是基于误解:

如果表达式即使给出where false子句也不会更改任何内容,则似乎没有采取锁定措施。

那不是Postgres的工作方式。无论采用哪种方式进行锁定(以上链接的答案中的解释),Postgres锁定机制甚至都不会考虑新行与旧行的比较。

不过,这是否真的需要取决于我的猜测,该值不会出现在行中?还是有更好的方法来确保您获得锁?

不,不。

如果确实存在缺少FK值的问题,则可以在带有rCTE的单个语句中添加缺失(不同的)值。像您演示的那样,对单行插入很简单,但是也可以一次插入很多行。

2021-03-17