当前位置: 首页 > 知识库问答 >
问题:

Postgresql递归更新jsonb键

陈季
2023-03-14

具有以下数据模型:

create table test
(
    id int primary key,
    js jsonb
);
insert into test values (1, '{"id": "total", "price": 400, "breakdown": [{"id": "product1", "price": 400}] }');
insert into test values (2, '{"id": "total", "price": 1000, "breakdown": [{"id": "product1", "price": 400}, {"id": "product2", "price": 600}]}');

我需要将所有价格键更新为新名称cost。在静态字段上很容易做到这一点,使用:

update test
set js = jsonb_set(js #- '{price}', '{cost}', js #> '{price}');

结果:

1 {"id": "total", "cost": 1000, "breakdown": [{"id": "product1", "price": 400}]}
2 {"id": "total", "cost": 2000, "breakdown": [{"id": "product1", "price": 400}, {"id": "product2", "price": 600}]}

但是我也需要在分解数组中做这些。

在不知道细分数组中的项目数的情况下,我如何做到这一点?换句话说,我如何在jsonb数组的每个元素上应用函数。

非常感谢。

共有3个答案

汝昀
2023-03-14

将所有jsonb对象中的jsonb键替换为jsonb数组的另一种方法是:

我的查询分解了jsonb数组。对于每个对象,如果price键存在,则从jsonb对象中删除price键,使用旧的price值添加新的键,然后使用修改后的对象创建一个新的jsonb数组。最后用新的替换旧的jsonb数组。

WITH cte AS (SELECT id, jsonb_agg(CASE WHEN item ? 'price' 
                                       THEN jsonb_set(item - 'price', '{"cost"}', item -> 'price')
                                       ELSE item END) AS cost_array
              FROM test
              CROSS JOIN jsonb_array_elements(js -> 'breakdown') WITH ORDINALITY arr(item, index)
              GROUP BY id)
              
UPDATE test
SET js = jsonb_set(js, '{breakdown}', cte.cost_array, false)
FROM cte
WHERE cte.id = test.id;

云何平
2023-03-14

此查询是示例,易于更改字段:

您可以在dbfiddle中看到我的查询结构

update test u_t
set js = tmp.new_js
from (
         select t.id,
                (t.js || jsonb_build_object('cost', t.js ->> 'price')) - 'price'
                    ||
                jsonb_build_object('breakdown', jsonb_agg(
                        (b.value || jsonb_build_object('cost', b.value ->> 'price')) - 'price')) as new_js
         from test t
                  cross join jsonb_array_elements(t.js -> 'breakdown') b
         group by t.id) tmp
where u_t.id = tmp.id;
袁谭三
2023-03-14

解决方案1:干净但沉重

首先创建一个类似于jsonb_set的聚合函数:

CREATE OR REPLACE FUNCTION jsonb_set(x jsonb, y jsonb, _path text[], _key text, _val jsonb, create_missing boolean DEFAULT True)
RETURNS jsonb LANGUAGE sql IMMUTABLE AS
$$
    SELECT jsonb_set(COALESCE(x, y), COALESCE(_path, '{}' :: text[]) || _key, COALESCE(_val, 'null' :: jsonb), create_missing) ;
$$ ;

DROP AGGREGATE IF EXISTS jsonb_set_agg (jsonb, text[], text, jsonb, boolean) CASCADE ;
CREATE AGGREGATE jsonb_set_agg (jsonb, text[], text, jsonb, boolean)
(
  sfunc = jsonb_set
, stype = jsonb
) ;

然后,在迭代jsonb数组元素时调用聚合函数:

WITH list AS (
SELECT id, jsonb_set_agg(js #- '{breakdown,' || ind || ',price}', '{breakdown,' || ind || ',cost}', js #> '{breakdown,' || ind || ',price}', true) AS js
FROM test
CROSS JOIN LATERAL generate_series(0, jsonb_array_length(js->'{breakdown}') - 1) AS ind
GROUP BY id)
UPDATE test AS t
SET js = jsonb_set(l.js #- '{price}', '{cost}', l.js #> '{price}')
FROM list AS l
WHERE t.id = l.id ;

解决方案2:又快又脏

您只需将jsonb转换为string,并将子字符串“price”替换为“cost”:

UPDATE test
SET js = replace(js :: text, 'price', 'cost') :: jsonb

在一般情况下,此解决方案将替换子字符串“价格”,即使在 jsonb 字符串值和包含子字符串“价格”的 jsonb 键中也是如此。为了降低风险,您可以将子字符串“price” :“替换为”成本“ :”,但风险仍然存在。

 类似资料:
  • 在我的Java项目中,我是usinq、JOOQ,我希望更新JSONB值。但我的建造失败了。 我写了一段代码: 我收到了下一条信息: 我犯错的地方?

  • 如何更新JSONB以将新键添加到所有记录的嵌套数组(用于数组的所有项目)中。 我指的是表结构的链接: 给定的json是: 要将新元素添加到每个数组项中,请执行以下查询: 执行上述查询后输出: 但是上面只会在id=1的地方更新json。对于orders中的所有行,需要进行哪些更改才能更新与上述相同的JSON?

  • 我正在使用(https://github.com/typeorm/typeorm)作为ORM库来连接postgresql数据库。我将几列保存为类型。现在我需要支持部分更新json数据,例如, 如果我只更新,我不想在更新语句中覆盖lastName。支持,我可以使用它来部分更新字段,但我如何使它与图书一起工作?

  • 我正在创建一个递归导航迷宫的程序。代码: 然而,每当我到达死胡同时,它都不会回溯。当我调试时,它表明当程序从递归或“回溯”返回时,我的起始值专注于停留在我的死胡同空间。 例如: 9是我的出发点。2是我的退出。4是我的道路。1 表示墙壁。当我到达一个死胡同时(在本例中为第 7 行,第 2 列)。我的立场是等于整个程序其余部分的死胡同空间。这是为什么呢?

  • 问题内容: PostgreSQL 9.1 经营状况 每个月,都会为特定的流程分配一批新的帐户。可以按月份,帐户数和帐户总余额来描述每批。该过程的目标是从客户那里收回一些余额。然后每月分别跟踪每个批次(自将批次转移到流程以来,每个月回收的金额)。 目标 我的目标是预测将来将回收多少。 资料定义 样本数据 计算过程 您可以将数据想象成一个三角形矩阵(将预测X值): 算法 我的目标是预测所有遗漏的点(未

  • 每次我执行INSERT或UPSERT(ON CONFLICT UPDATE)时,每个表上的增量列都会增加之前的更新数量。 例如,如果我有这张表: 然后运行这些查询: 这是一个相当大的问题。我们运行的脚本每天处理100000个通知。这会在每一次插入之间产生大约10000行的间隙,所以我们可能从100行开始,但当我们达到1000行时,最后一行的主键ID值会自动增加到100000以上。 如果这种情况继续