使用Postgres9.6,我遵循了推荐的策略来执行INSERTor或SELECT返回结果ID:
INSERT
SELECT
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子句也不会改变任何内容,则似乎没有采取锁定措施。不过,这是否真的需要取决于我的猜测,该值不会出现在行中?还是有更好的方法来确保您获得锁?
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。
您的观察 似乎 不可能。上面的命令应 始终 为新插入的行或先前存在的行返回一个ID。并发写入不会对此造成混乱,因为现有的冲突行已被锁定。相关答案中的解释:
__当然, 除非 引发异常 。在这种情况下,您会收到一条错误消息,而不是结果。你检查了吗?您是否有错误处理?(以防您的应用以某种方式丢弃错误消息:1)修复此问题。2)数据库日志中还有一个附加条目,具有默认的日志记录设置。)
我确实在您的表定义中看到了FK约束:
prop_type text not null references prop_type(name),
如果您尝试插入违反约束的行,则确实会发生这种情况。如果name = 'jargon'table中没有任何行,prop_type那么您将得到:
name = 'jargon'
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”.
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的单个语句中添加缺失(不同的)值。像您演示的那样,对单行插入很简单,但是也可以一次插入很多行。