如何使用PostgreSQL更新jsonb字符串?

如何使用PostgreSQL更新jsonb字符串?,json,postgresql,postgresql-9.4,jsonb,nosql,Json,Postgresql,Postgresql 9.4,Jsonb,Nosql,我正在使用PostgreSQL 9.4.5。我想更新ajsonb专栏 我的表格的结构如下: CREATE TABLE my_table ( gid serial PRIMARY KEY, "data" jsonb ); JSON字符串如下所示: {"files": [], "ident": {"id": 1, "country": null, "type ": "20"}} 以下SQL不执行此任务(语法错误-SQL状态=42601): 有没有办法做到这一点?我以前的解决方案依赖

我正在使用PostgreSQL 9.4.5。我想更新a
jsonb
专栏

我的表格的结构如下:

CREATE TABLE my_table (
  gid    serial PRIMARY KEY,
  "data" jsonb
);
JSON字符串如下所示:

{"files": [], "ident": {"id": 1, "country": null, "type ": "20"}}
以下SQL不执行此任务(语法错误-SQL状态=42601):


有没有办法做到这一点?

我以前的解决方案依赖于9.5功能


我建议您要么使用下面的abelisto解决方案,要么使用pl/perlu、plpythonu或plv8js,用一种对json变体有更好支持的语言编写json变体。

在第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
子句中,
将“数据”->“标识”
解压缩到记录中(您需要
为其创建类型
结构)。然后,它被构建回一个带有新国家名称的JSON对象中。在
UPDATE
中,将
的“ident”
对象与
的“files”
对象重新连接,并将整个对象转换为
jsonb


一个纯粹的美的东西——只要速度不是你的东西……

好的,有两个功能:

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)

第二种方法是使用指定的路径更改现有密钥的值,或者在路径不存在时创建它:

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上测试

更新my_表集“data”=jsonb_集(“data”、“{”ident“,”country“}”和“,”比利时“)
实际重复:我同意,但你提到的帖子中的问题是关于
json
类型(而不是
jsonb
)。答案通常指的是
jsonb
。@Abelisto:您的查询返回错误消息:函数jsonb_集(jsonb,未知,未知)不存在
jsonb_set()
函数对v来说似乎是新函数。是的,我错过了
postgresql-9.4
标签<9.5版本中引入了code>jsonb_集功能。肮脏但简单的方式:
。。。设置“data”=regexp_replace(“data”::text,“country”:[^,^}]+',“country”:“belling”)::jsonb
您的查询返回错误消息:fonction jsonb_build_object(未知,未知)不存在抱歉,误读了文档,应该是json_build_object,然后将最终结果强制转换到jsonb。editing.hmmm | |也是仅9.5版本。IMO您的解决方案的主要缺点是,您在重建整个对象时并没有注意到其可能不同的初始结构。
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)