2016-09-27 7 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

最良のオプションは、9.5オファーをpostgresに新しいアップサートを使用することですが、これは(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 *; 

これはあなたのアプローチにはない(おそらく、これを行うための最速の方法であり、2つのプロセスが同時に同じテーブルにアップサートしようとした場合、予期しない、何も起こらないことを保証しますそれを保証する)。

関連する問題