更新 JSONB 格式的对象数组 PostgreSQL

问题描述

我需要执行两个更新操作:

  1. 在第一种情况下将更改数据从字符串更新为布尔值
  2. 根据其他标签使用新名称和数据重新创建标签

jsonb 列中的示例数据:

[
    { "tax": "yes","tax_percent": 20,"used_when": "after" },{ "tax": "no","tax_percent": 20 },{ "tax_percent": 20,"used_when": "before" }
]

现在:

"tax" 值需要更新为 yes -> true,no OR null(不存在)表示 -> false

“used_when”需要更新为“using”,如果之后->真,如果之前或空(不存在)意味着->假

所以它看起来像:

[
    { "tax": true,"using": true },{ "tax": false,"using": false },"using": false }
]

这些值是可选的,因此并非所有条目都有它,而且这是数据库列中的单行,因此需要为每一行更新列中的数据。

解决方法

step-by-step demo:db<>fiddle

UPDATE t                                                                   -- 5
SET mydata = s.new_data
FROM (
    SELECT
        id,json_agg((using_updated - 'used_when')::json) as new_data          -- 4
    FROM t,json_array_elements(t.mydata) as elements,-- 1
        jsonb_set(elements::jsonb,'{tax}',-- 2
            CASE
                WHEN elements ->> 'tax' = 'yes' THEN 'true'::jsonb
                ELSE 'false'::jsonb
            END
        ) as tax_updated,jsonb_set(tax_updated::jsonb,'{using}',-- 3
            CASE
                WHEN tax_updated ->> 'used_when' = 'true' THEN 'true'::jsonb
                ELSE 'false'::jsonb
            END
        ) as using_updated
    GROUP BY id
) s
WHERE s.id = t.id;
  1. 将所有数组元素提取为每条记录一个元素 2/3。现在您可以使用 jsonb_set() 在数组元素中插入新的或更新现有的属性。 CASE 子句进行条件检查
  2. 消除剩余的 used_when 元素。之后,您可以使用 json_agg 重新聚合更新的元素
  3. UPDATE

如果您的数据是 json 类型,您必须对 jsonb 进行强制转换,因为没有 json_set()。如果没有,您当然可以忽略演员表。

但是,正如@a_horse_with_no_name 正确提到的那样:您应该考虑不将这些数据存储为纯 JSON,而是将它们提取到规范化的相关数据库表结构中,这会使事情变得更加简单和高效。