使用 jsonb_set() 更新特定的 jsonb 数组值

Posted

技术标签:

【中文标题】使用 jsonb_set() 更新特定的 jsonb 数组值【英文标题】:Using jsonb_set() for updating specific jsonb array value 【发布时间】:2016-12-24 02:04:49 【问题描述】:

目前我正在使用 PostgreSQL 9.5 并尝试更新 jsonb 字段数组中的值。但我无法获取所选值的索引

我的桌子是这样的:

 CREATE TABLE samples (
    id serial,
    sample jsonb
 );

我的 JSON 如下所示:

"result": [
    "8410": "ABNDAT", "8411": "Abnahmedatum",
    "8410": "ABNZIT", "8411": "Abnahmezeit",
    "8410": "FERR_R", "8411": "Ferritin"
]

我的 SELECT 语句可以得到正确的值:

SELECT 
    id, value 
FROM 
    samples s, jsonb_array_elements(s.sample#>'result') r  
WHERE 
    s.id = 26 and r->>'8410' = 'FERR_R';

结果:

id | value
----------------------------------------------
26 | "8410": "FERR_R", "8411": "Ferritin"

好的,这就是我想要的。现在我想使用以下 UPDATE 语句执行更新以添加新元素“ratingtext”(如果还没有):

UPDATE 
    samples s
SET
    sample = jsonb_set(sample,
              'result,2,ratingtext',
              '"Some individual text"'::jsonb,
              true)
WHERE
      s.id = 26;

执行UPDATE语句后,我的数据是这样的(也正确):

"result": [
    "8410": "ABNDAT", "8411": "Abnahmedatum",
    "8410": "ABNZIT", "8411": "Abnahmezeit",
    "8410": "FERR_R", "8411": "Ferritin", "ratingtext": "Some individual text"
]

到目前为止一切顺利,但我手动搜索了 2 的索引值以获取 JSON 数组中的正确元素。如果要更改顺序,这将不起作用。

所以我的问题:

有没有办法获取选中的 JSON 数组元素的索引,并将 SELECT 语句和 UPDATE 语句合二为一?

就像:

UPDATE 
    samples s
SET
    sample = jsonb_set(sample,
              'result,' || INDEX OF ELEMENT || ',ratingtext',
              '"Some individual text"'::jsonb,
              true)
WHERE
      s.id = 26;

samples.id"8410" 的值在准备语句之前是已知的。

或者这暂时不可能?

【问题讨论】:

类似问题/答案:dba.stackexchange.com/questions/193390/… 【参考方案1】:

您可以使用jsonb_array_elements() with ordinality 找到搜索元素的索引(注意,ordinality 从 1 开始,而 json 数组的第一个索引是 0):

select 
    pos- 1 as elem_index
from 
    samples, 
    jsonb_array_elements(sample->'result') with ordinality arr(elem, pos)
where
    id = 26 and
    elem->>'8410' = 'FERR_R';

 elem_index 
------------
          2
(1 row) 

使用上面的查询根据它的索引更新元素(注意jsonb_set()的第二个参数是一个文本数组):

update 
    samples
set
    sample = 
        jsonb_set(
            sample,
            array['result', elem_index::text, 'ratingtext'],
            '"some individual text"'::jsonb,
            true)
from (
    select 
        pos- 1 as elem_index
    from 
        samples, 
        jsonb_array_elements(sample->'result') with ordinality arr(elem, pos)
    where
        id = 26 and
        elem->>'8410' = 'FERR_R'
    ) sub
where
    id = 26;    

结果:

select id, jsonb_pretty(sample)
from samples;

 id |                   jsonb_pretty                   
----+--------------------------------------------------
 26 |                                                +
    |     "result": [                                 +
    |                                                +
    |             "8410": "ABNDAT",                   +
    |             "8411": "Abnahmedatum"              +
    |         ,                                      +
    |                                                +
    |             "8410": "ABNZIT",                   +
    |             "8411": "Abnahmezeit"               +
    |         ,                                      +
    |                                                +
    |             "8410": "FERR_R",                   +
    |             "8411": "Ferritin",                 +
    |             "ratingtext": "Some individual text"+
    |                                                +
    |     ]                                           +
    | 
(1 row)

jsonb_set() 中的最后一个参数应该是true,以在其键尚不存在时强制添加新值。但是可以跳过它,因为它的默认值为true

虽然似乎不太可能出现并发问题(由于 WHERE 条件的限制性和受影响的行数可能较少),但您可能也对Atomic UPDATE .. SELECT in Postgres. 感兴趣

【讨论】:

非常感谢您的回答!它就像一个魅力!我没有意识到,我可以在 WHERE 子句中使用 elem 感谢克林,这救了我的命! 通过将其分解为多个部分进行了很好的解释,这当然值得更多的支持 @klin,缺少的一件事可能是潜在的并发问题,请参阅dba.stackexchange.com/questions/69471/postgres-update-limit-1/…,还有为什么在jsonb_set 中使用true @ChristopheRoussy - 谢谢,我已经添加了相应的注释。

以上是关于使用 jsonb_set() 更新特定的 jsonb 数组值的主要内容,如果未能解决你的问题,请参考以下文章

如何在具有空值的列上使用`jsonb_set`

JPA 和 JSON 运算符本机更新查询

postgresql jsonb 操作

Sqlalchemy 使用 json_set 更新特定的 JSON 字段

使用 postgres 9.4 将 JSON 元素附加到数组

在 AngularJS 中的 JSON 中添加、删除和更新特定数据