我正在使用PostgreSQL 9.4.5。我想更新 jsonb
列。
我的表格是这样构建的:
CREATE TABLE my_table (
gid serial PRIMARY KEY,
"data" jsonb
);
JSON字符串是这样的:
{"files": [], "ident": {"id": 1, "country": null, "type ": "20"}}
以下SQL不执行此任务(语法错误 - SQL状态= 42601):
UPDATE my_table SET "data" -> 'ident' -> 'country' = 'Belgium';
有没有办法实现这个目标?
答案 0 :(得分:1)
好的有两个功能:
create or replace function set_jsonb_value(p_j jsonb, p_key text, p_value jsonb) returns jsonb as $$
select jsonb_object_agg(t.key, t.value) from (
select
key,
case
when jsonb_typeof(value) = 'object' then set_jsonb_value(value, p_key, p_value)
when key = p_key then p_value
else value
end as value from jsonb_each(p_j)) as t;
$$ language sql immutable;
首先只需更改现有密钥的值,而不管密钥路径如何:
postgres=# select set_jsonb_value(
'{"files": [], "country": null, "ident": {"id": 1, "country": null, "type ": "20"}}',
'country',
'"foo"');
set_jsonb_value
--------------------------------------------------------------------------------------
{"files": [], "ident": {"id": 1, "type ": "20", "country": "foo"}, "country": "foo"}
(1 row)
create or replace function set_jsonb_value(p_j jsonb, p_path text[], p_value jsonb) returns jsonb as $$
select jsonb_object_agg(t.key, t.value) from (
select
key,
case
when jsonb_typeof(value) = 'object' then set_jsonb_value(value, p_path[2:1000], p_value)
when key = p_path[1] then p_value
else value
end as value from jsonb_each(p_j)
union all
select
p_path[1],
case
when array_length(p_path,1) = 1 then p_value
else set_jsonb_value('{}', p_path[2:1000], p_value) end
where not p_j ? p_path[1]) as t;
$$ language sql immutable;
第二个使用指定的路径更改现有密钥的值,或者如果路径不存在则创建它:
postgres=# select set_jsonb_value(
'{"files": [], "country": null, "ident": {"id": 1, "type ": "20"}}',
'{ident,country}'::text[],
'"foo"');
set_jsonb_value
-------------------------------------------------------------------------------------
{"files": [], "ident": {"id": 1, "type ": "20", "country": "foo"}, "country": null}
(1 row)
postgres=# select set_jsonb_value(
'{"files": [], "country": null, "ident": {"id": 1, "type ": "20"}}',
'{ident,foo,bar,country}'::text[],
'"foo"');
set_jsonb_value
-------------------------------------------------------------------------------------------------------
{"files": [], "ident": {"id": 1, "foo": {"bar": {"country": "foo"}}, "type ": "20"}, "country": null}
(1 row)
希望它对使用PostgreSQL的人有所帮助< 9.5
免责声明:在PostgreSQL 9.5上进行测试
答案 1 :(得分:0)
在PG 9.4中你运气不好"轻松"像jsonb_set()
(9.5)这样的解决方案。您唯一的选择是解压缩JSON对象,进行更改并重新构建对象。听起来非常麻烦,确实如此:无论内置功能多么先进或精细,JSON操作起来都很糟糕。
CREATE TYPE data_ident AS (id integer, country text, "type" integer);
UPDATE my_table
SET "data" = json_build_object('files', "data"->'files', 'ident', ident.j)::jsonb
FROM (
SELECT gid, json_build_object('id', j.id, 'country', 'Belgium', 'type', j."type") AS j
FROM my_table
JOIN LATERAL jsonb_populate_record(null::data_ident, "data"->'ident') j ON true) ident
WHERE my_table.gid = ident.gid;
在SELECT
子句中"data"->'ident'
被解压缩到一个记录中(你需要CREATE TYPE
一个结构)。然后使用新的国家/地区名称将其重新构建到JSON对象中。在UPDATE
"ident"
对象与"files"
对象重新加入,整个事物转换为jsonb
。
纯粹的美丽事物 - 只要速度不是你的事情......
答案 2 :(得分:-1)
我之前的解决方案依赖于9.5功能。
我建议改为使用下面的abelisto解决方案或使用pl / perlu,plpythonu或plv8js以更好的支持语言编写json mutators。