需要从 upsert 返回的行

Need row returned from upsert

我有一个 table 需要更新。如果该行已经存在,那么我想更新并 return 该行。如果该行尚不存在,那么我需要插入并 return 该行。通过下面的查询,我在插入时得到行 returned,但在更新时没有。

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 查询中处理。此外,对于行的唯一性,table 没有约束,否则我会使用 on conflict。但如果需要,我愿意放弃这个查询并使用其他东西。

这些是我 运行 查询然后再次 运行 查询时的结果。您可以看到,在插入或第一个 运行 中,我得到了行 returned。但是,在随后的 运行 查询中,我得到 0 行 returned。我知道它正在工作,因为 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

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

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
;

此处最好的选择是使用 postgres 9.5 提供的新更新插入,但这需要 (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 *;

这可能是执行此操作的最快方法,并且可以保证如果两个进程同时尝试更新插入到同一个 table 中不会发生任何意外(您的方法不能保证这一点)。