2016-09-27 72 views
2

我有一個表,我需要upsert。如果該行已經存在,那麼我想更新並返回該行。如果該行尚不存在,那麼我需要插入並返回該行。通過下面的查詢,我得到了插入時返回的行,但沒有更新。需要從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 
+0

你使用的是什麼版本的postgres?另外,似乎'(message_id,account_id,field_config_id)'的組合是唯一的,正確的? – redneb

+0

版本是9.5.3,組合是唯一的,但沒有唯一約束,我現在無法添加它。這會讓事情變得更容易。 – thepriebe

+0

一個獨特的索引會讓思考變得更容易。另外,還不清楚你希望它做什麼更新:目前在你的代碼中,你正在爲3列分配他們已有的值,所以這將不起作用。 – redneb

回答

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 
; 
+0

工程太棒了!感謝您的解決方案! – thepriebe

1

這裏最好的選擇是使用新的UPSERT 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 *; 

這可能是這樣做的最快方法,並保證如果兩個進程試圖在同一時間UPSERT到同一個表無意外會發生(你的方法不保證)。