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

hw-*_*135 0 sql postgresql json upsert postgresql-10

我有一个 Postgres 表messages如下:

Column   |           Type             | Collation | Nullable |                    
-----------+--------------------------+-----------+----------
 id        | integer                  |           | not null |
 message   | jsonb                    |           |          | 
 date      | timestamp with time zone |           | not null | 
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

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

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

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

我不知道这是否是正确的方法,所以如果有更好的方法来做到这一点,我将不胜感激。

小智 5

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

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)
;
Run Code Online (Sandbox Code Playgroud)

如果 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')
;
Run Code Online (Sandbox Code Playgroud)