繁体   English   中英

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

[英]INSERT or SELECT strategy to always return a row?

使用Postgres 9.6,我遵循了https://stackoverflow.com/a/40325406/435563中推荐的策略来执行INSERTSELECT并返回生成的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。

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

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

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

 prop_type text not null references prop_type(name), 

如果您尝试插入违反约束的行,这就是发生的情况。 如果在表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". 

演示:

dbfiddle 在这里

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

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

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

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

那不是Postgres的工作方式。 可以采用任何一种方式进行锁定(以上链接的答案中有解释),并且Postgres锁定机制从未考虑过新行与旧行的比较。

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

不,不。

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

https://www.postgresql.org/docs/9.5/static/sql-insert.html

ON CONFLICT DO UPDATE保证原子的INSERT或UPDATE结果; 如果没有独立的错误,即使在高并发情况下,也可以保证这两个结果之一。

这与您更新后的帖子中的锁youmention有关。 现在,关于返回行的初始问题-我先仔细阅读了它。 现在,我看到了where falsewhere false -使用此子句,并非总是返回一行。 例如:

t=# create table a(i int, e int);
CREATE TABLE
t=# insert into a select 1,1;
INSERT 0 1
t=# create unique index b on a (i);
CREATE INDEX
---now insert on conflict do nothing:
t=# insert into a select 1,1 on conflict do nothing returning *,xmax,xmin;
 i | e | xmax | xmin
---+---+------+------
(0 rows)

INSERT 0 0
-- where false same effect - no rows
t=# insert into a select 1,1 on conflict(i) do update set e=2 where false returning *,xmax,xmin;
 i | e | xmax | xmin
---+---+------+------
(0 rows)
-- now insert without conflict:
t=# insert into a select 2,2 on conflict(i) do update set e=2 where EXCLUDED.e=1 returning *,xmax;
 i | e | xmax
---+---+------
 2 | 2 |    0
(1 row)
-- now insert with update on conflict:
INSERT 0 1
t=# insert into a select 1,1 on conflict(i) do update set e=2 where EXCLUDED.e=1 returning *,xmax;
 i | e |   xmax
---+---+-----------
 1 | 2 | 126943767
(1 row)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM