需要从upsert返回的行

时间:2016-09-27 17:54:02

标签: postgresql upsert

我有一张桌子需要插件。如果该行已存在,那么我想更新并返回该行。如果该行还没有存在,那么我需要插入并返回该行。通过下面的查询,我得到插入时返回的行,但不会更新。

Table "main.message_account_seen"
    Column      |           Type           |                             Modifiers                             
----------------+--------------------------+-------------------------------------------------------------------
id              | integer                  | not null default nextval('message_account_seen_id_seq'::regclass)
field_config_id | integer                  | not null
edit_stamp      | timestamp with time zone | not null default now()
audit_stamp     | timestamp with time zone | 
message_id      | integer                  | not null
account_id      | integer                  | 

这是sql。

with upsert as (
    update message_account_seen set (message_id, account_id, field_config_id ) = (1, 60, 980) 
    where message_id = 1 and account_id = 60 and field_config_id = 980 returning *
)
insert into message_account_seen (message_id, account_id, field_config_id)
select 1, 60, 980
where not exists (select message_id, account_id, field_config_id from upsert) returning *;

我不能做postgres函数,需要在常规的sql查询中处理。此外,表的唯一性没有约束,否则我会使用on conflict。但我愿意废弃此查询,并在需要时使用其他内容。

这些是我运行查询然后再次运行它时的结果。你可以看到在插入或第一次运行时我得到了返回的行。但是在后续运行的查询中,我得到0行返回。我知道它正在工作,因为edit_stamp会随着时间的推移而增加。这是一件好事。

# with upsert as (
    update message_account_seen set (message_id, account_id, field_config_id ) = (1, 60, 980) 
    where message_id = 1 and account_id = 60 and field_config_id = 980 returning *
)
insert into message_account_seen (message_id, account_id, field_config_id)
select 1, 60, 980
where not exists (select message_id, account_id, field_config_id from upsert) returning *;
id | field_config_id |           edit_stamp           | audit_stamp | message_id | account_id 
 --+-----------------+--------------------------------+-------------+------------+------------
38 |             980 | 09/27/2016 11:43:22.153908 MDT |             |          1 |         60
(1 row)

INSERT 0 1
# with upsert as (
    update message_account_seen set (message_id, account_id, field_config_id ) = (1, 60, 980) 
    where message_id = 1 and account_id = 60 and field_config_id = 980 returning *
)
insert into message_account_seen (message_id, account_id, field_config_id)
select 1, 60, 980
where not exists (select message_id, account_id, field_config_id from upsert) returning *;
id | field_config_id | edit_stamp | audit_stamp | message_id | account_id 
----+-----------------+------------+-------------+------------+------------
(0 rows)

INSERT 0 0

2 个答案:

答案 0 :(得分:2)

更新成功后,查询中不会返回结果。这样做:

with upsert as (
    update message_account_seen
    set (message_id, account_id, field_config_id ) = (1, 60, 980) 
    where (message_id, account_id, field_config_id) = (1, 60, 980)
    returning *
), ins as (
    insert into message_account_seen (message_id, account_id, field_config_id)
    select 1, 60, 980
    where not exists (select 1 from upsert)
    returning *
)
select * from upsert
union all
select * from ins
;

答案 1 :(得分:1)

这里最好的选择是使用postgres 9.5提供的新upsert,但这需要(message_id, account_id, field_config_id)上的唯一索引。它可以像这样使用:

INSERT INTO message_account_seen(message_id, account_id, field_config_id)
VALUES (1, 60, 980)
ON CONFLICT (message_id, account_id, field_config_id)
DO UPDATE
SET edit_stamp=now() -- adjust here
RETURNING *;

这可能是执行此操作的最快方法,并保证如果两个进程同时尝试插入同一个表中,则不会发生任何意外情况(您的方法无法保证)。