如何使用 where 子句修改或删除存储在 PostgreSQL 中 jsonb 列类型中的 JSON 数组中的特定 JSON 对象?
How to modify or remove a specific JSON object from JSON array stored in jsonb column type in PostgreSQL using where clause?
在我的 Postgres 数据库中,我有一个 table 列具有 jsonb 数据类型。在该列中,我存储了 JSON 数组。现在,我想删除或修改数组中的特定 JSON 对象。
我的 JSON 数组看起来像
[
{
"ModuleId": 1,
"ModuleName": "XYZ"
},
{
"ModuleId": 2,
"ModuleName": "ABC"
}
]
现在,我要执行两个操作:
- 如何从上述数组中删除 ModuleId 为 1 的 JSON 对象?
- 如何修改 JSON 对象,即将 ModuleName 更改为 'CBA',其 ModuleId 为 1?
有没有一种方法可以直接在 JSON 数组上执行查询?
注:Postgres 版本为 12.0
这两个问题都需要取消嵌套并聚合回(修改后的)JSON 元素。对于这两个问题,我会创建一个函数以使其更易于使用。
create function remove_element(p_value jsonb, p_to_remove jsonb)
returns jsonb
as
$$
select jsonb_agg(t.element order by t.idx)
from jsonb_array_elements(p_value) with ordinality as t(element, idx)
where not t.element @> p_to_remove;
$$
language sql
immutable;
函数可以这样使用,例如在更新语句中:
update the_table
set the_column = remove_element(the_column, '{"ModuleId": 1}')
where ...
对于第二个问题,类似的函数就派上用场了。
create function change_value(p_value jsonb, p_what jsonb, p_new jsonb)
returns jsonb
as
$$
select jsonb_agg(
case
when t.element @> p_what then t.element||p_new
else t.element
end order by t.idx)
from jsonb_array_elements(p_value) with ordinality as t(element, idx);
$$
language sql
immutable;
||
运算符将覆盖现有密钥,因此这有效地将旧名称替换为新名称。
你可以这样使用它:
update the_table
set the_column = change_value(the_column, '{"ModuleId": 1}', '{"ModuleName": "CBA"}')
where ...;
我认为传递 JSON 值比硬编码键更灵活一些,这使得函数的使用非常有限。第一个函数也可用于通过比较多个键来删除数组元素。
如果您不想创建函数,请将函数调用替换为函数中的 select
。
对于这两种情况,请考虑使用包含动态逻辑的子查询来确定包含 ModuleId 键值等于 1 的元素的索引。
对于第一种情况,使用#-
运算符:
WITH s AS
(
SELECT ('{'||idx-1||'}')::text[] AS path, jsdata
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'ModuleId'='1'
)
UPDATE tab
SET jsdata = s.jsdata #- path
FROM s
对于第二种情况,使用 jsonb_set()
函数和来自子查询的路径:
WITH s AS
(
SELECT ('{'||idx-1||',ModuleId}')::text[] AS path
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'ModuleId'='1'
)
UPDATE tab
SET jsdata = jsonb_set(jsdata,s.path,'"CBA"',false)
FROM s
在我的 Postgres 数据库中,我有一个 table 列具有 jsonb 数据类型。在该列中,我存储了 JSON 数组。现在,我想删除或修改数组中的特定 JSON 对象。
我的 JSON 数组看起来像
[
{
"ModuleId": 1,
"ModuleName": "XYZ"
},
{
"ModuleId": 2,
"ModuleName": "ABC"
}
]
现在,我要执行两个操作:
- 如何从上述数组中删除 ModuleId 为 1 的 JSON 对象?
- 如何修改 JSON 对象,即将 ModuleName 更改为 'CBA',其 ModuleId 为 1?
有没有一种方法可以直接在 JSON 数组上执行查询?
注:Postgres 版本为 12.0
这两个问题都需要取消嵌套并聚合回(修改后的)JSON 元素。对于这两个问题,我会创建一个函数以使其更易于使用。
create function remove_element(p_value jsonb, p_to_remove jsonb)
returns jsonb
as
$$
select jsonb_agg(t.element order by t.idx)
from jsonb_array_elements(p_value) with ordinality as t(element, idx)
where not t.element @> p_to_remove;
$$
language sql
immutable;
函数可以这样使用,例如在更新语句中:
update the_table
set the_column = remove_element(the_column, '{"ModuleId": 1}')
where ...
对于第二个问题,类似的函数就派上用场了。
create function change_value(p_value jsonb, p_what jsonb, p_new jsonb)
returns jsonb
as
$$
select jsonb_agg(
case
when t.element @> p_what then t.element||p_new
else t.element
end order by t.idx)
from jsonb_array_elements(p_value) with ordinality as t(element, idx);
$$
language sql
immutable;
||
运算符将覆盖现有密钥,因此这有效地将旧名称替换为新名称。
你可以这样使用它:
update the_table
set the_column = change_value(the_column, '{"ModuleId": 1}', '{"ModuleName": "CBA"}')
where ...;
我认为传递 JSON 值比硬编码键更灵活一些,这使得函数的使用非常有限。第一个函数也可用于通过比较多个键来删除数组元素。
如果您不想创建函数,请将函数调用替换为函数中的 select
。
对于这两种情况,请考虑使用包含动态逻辑的子查询来确定包含 ModuleId 键值等于 1 的元素的索引。
对于第一种情况,使用#-
运算符:
WITH s AS
(
SELECT ('{'||idx-1||'}')::text[] AS path, jsdata
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'ModuleId'='1'
)
UPDATE tab
SET jsdata = s.jsdata #- path
FROM s
对于第二种情况,使用 jsonb_set()
函数和来自子查询的路径:
WITH s AS
(
SELECT ('{'||idx-1||',ModuleId}')::text[] AS path
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'ModuleId'='1'
)
UPDATE tab
SET jsdata = jsonb_set(jsdata,s.path,'"CBA"',false)
FROM s