根据 Postgres 10 中 JSON 的特定值执行 UPSERT

Do UPSERT based on specific value of JSON in Postgres 10

我有一个 Postgres table messages 如下:

Column   |           Type             | Collation | Nullable |                    
-----------+--------------------------+-----------+----------
 id        | integer                  |           | not null |
 message   | jsonb                    |           |          | 
 date      | timestamp with time zone |           | not null | 
id | message                      | date
1  | {"name":"alpha", "pos":"x"}  | 2020-02-11 12:31:44.658667+00
2  | {"name":"bravo", "pos":"y"}  | 2020-02-11 12:32:43.123678+00
3  | {"name":"charlie", "pos":"z"}| 2020-02-11 12:38:37.623535+00

我想做的是根据 name 键的值做一个 UPSERT 即,如果有一个具有相同 name 值的插入,那么另一个值 pos 被更新,否则创建一个新条目。

我做到了CREATE UNIQUE INDEX message_name ON messages((message->>'name'));

我在 Postgres 9.5+ 中找到了 INSERT ON CONFLICT,但我不明白如何使用唯一索引。

首先我不知道这是否是正确的方法,所以如果有更好的方法,我将不胜感激。

您需要从索引中重复表达式:

insert into messages (message)
values ('{"name":"alpha", "pos":"new pos"}')
on conflict ((message->>'name')) 
  do update 
     set message = jsonb_set(messages.message, '{pos}'::text[], excluded.message -> 'pos', true)
;

如果您在 JSON 中有更多的键并且想要替换(或添加)所有这些,您可以使用这个:

insert into messages (message)
values ('{"name":"alpha", "pos":"new pos", "some key": 42}')
on conflict ((message->>'name')) 
  do update 
     set message = messages.message || (excluded.message - 'name')
;