如何修改新PostgreSQL JSON数据类型中的字段?

use*_*645 199 postgresql json postgresql-9.3 postgresql-json

使用postgresql 9.3我可以选择JSON数据类型的特定字段,但是如何使用UPDATE修改它们?我在postgresql文档中或在线任何地方都找不到任何这样的例子.我试过了明显的事:

postgres=# create table test (data json);
CREATE TABLE
postgres=# insert into test (data) values ('{"a":1,"b":2}');
INSERT 0 1
postgres=# select data->'a' from test where data->>'b' = '2';
 ?column?
----------
 1
(1 row)
postgres=# update test set data->'a' = to_json(5) where data->>'b' = '2';
ERROR:  syntax error at or near "->"
LINE 1: update test set data->'a' = to_json(5) where data->>'b' = '2...
Run Code Online (Sandbox Code Playgroud)

poz*_*ozs 289

更新:使用PostgreSQL 9.5,jsonbPostgreSQL本身有一些操作功能(但没有json;操作json值需要强制转换).

合并2个(或更多)JSON对象(或连接数组):

SELECT jsonb '{"a":1}' || jsonb '{"b":2}', -- will yield jsonb '{"a":1,"b":2}'
       jsonb '["a",1]' || jsonb '["b",2]'  -- will yield jsonb '["a",1,"b",2]'
Run Code Online (Sandbox Code Playgroud)

因此,设置一个简单的密钥可以使用:

SELECT jsonb '{"a":1}' || jsonb_build_object('<key>', '<value>')
Run Code Online (Sandbox Code Playgroud)

哪里<key>应该是字符串,并且<value>可以是任何类型to_jsonb()接受.

在JSON层次结构中深入设置值,jsonb_set()可以使用以下函数:

SELECT jsonb_set('{"a":[null,{"b":[]}]}', '{a,1,b,0}', jsonb '{"c":3}')
-- will yield jsonb '{"a":[null,{"b":[{"c":3}]}]}'
Run Code Online (Sandbox Code Playgroud)

完整参数列表jsonb_set():

jsonb_set(target         jsonb,
          path           text[],
          new_value      jsonb,
          create_missing boolean default true)
Run Code Online (Sandbox Code Playgroud)

path也可以包含JSON数组索引,并且从JSON数组的末尾开始计算出的负整数.但是,不存在但正面的JSON数组索引会将元素追加到数组的末尾:

SELECT jsonb_set('{"a":[null,{"b":[1,2]}]}', '{a,1,b,1000}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}'
Run Code Online (Sandbox Code Playgroud)

对于插入JSON数组(同时保留所有原始值),jsonb_insert()可以使用该函数(在9.6+;仅此功能,在本节中):

SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2')
-- will yield jsonb '{"a":[null,{"b":[2,1]}]}', and
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2', true)
-- will yield jsonb '{"a":[null,{"b":[1,2]}]}'
Run Code Online (Sandbox Code Playgroud)

完整参数列表jsonb_insert():

jsonb_insert(target       jsonb,
             path         text[],
             new_value    jsonb,
             insert_after boolean default false)
Run Code Online (Sandbox Code Playgroud)

同样,path从JSON数组末尾开始计数的负整数.

所以,f.ex.附加到JSON数组的末尾可以通过以下方式完成:

SELECT jsonb_insert('{"a":[null,{"b":[1,2]}]}', '{a,1,b,-1}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}', and
Run Code Online (Sandbox Code Playgroud)

但是,jsonb_set()pathin target是JSON对象的键时,此函数的工作方式略有不同(比).在这种情况下,它只会在未使用密钥时为JSON对象添加新的键值对.如果使用它,将引发错误:

SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,c}', jsonb '[2]')
-- will yield jsonb '{"a":[null,{"b":[1],"c":[2]}]}', but
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b}', jsonb '[2]')
-- will raise SQLSTATE 22023 (invalid_parameter_value): cannot replace existing key
Run Code Online (Sandbox Code Playgroud)

从JSON对象(或从数组中)删除键(或索引)可以通过-运算符完成:

SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
       jsonb '["a",1,"b",2]' - 1    -- will yield jsonb '["a","b",2]'
Run Code Online (Sandbox Code Playgroud)

可以通过#-运算符从JSON层次结构中深入删除:

SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
-- will yield jsonb '{"a":[null,{"b":[]}]}'
Run Code Online (Sandbox Code Playgroud)

对于9.4,您可以使用原始答案的修改版本(如下所示),但不是聚合JSON字符串,而是可以直接聚合到json对象中json_object_agg().

原始答案:纯SQL中也可以(没有plpython或plv8)(但需要9.3+,不适用于9.2)

CREATE OR REPLACE FUNCTION "json_object_set_key"(
  "json"          json,
  "key_to_set"    TEXT,
  "value_to_set"  anyelement
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
  FROM (SELECT *
          FROM json_each("json")
         WHERE "key" <> "key_to_set"
         UNION ALL
        SELECT "key_to_set", to_json("value_to_set")) AS "fields"
$function$;
Run Code Online (Sandbox Code Playgroud)

SQLFiddle

编辑:

一个版本,设置多个键和值:

CREATE OR REPLACE FUNCTION "json_object_set_keys"(
  "json"          json,
  "keys_to_set"   TEXT[],
  "values_to_set" anyarray
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
  FROM (SELECT *
          FROM json_each("json")
         WHERE "key" <> ALL ("keys_to_set")
         UNION ALL
        SELECT DISTINCT ON ("keys_to_set"["index"])
               "keys_to_set"["index"],
               CASE
                 WHEN "values_to_set"["index"] IS NULL THEN 'null'::json
                 ELSE to_json("values_to_set"["index"])
               END
          FROM generate_subscripts("keys_to_set", 1) AS "keys"("index")
          JOIN generate_subscripts("values_to_set", 1) AS "values"("index")
         USING ("index")) AS "fields"
$function$;
Run Code Online (Sandbox Code Playgroud)

编辑2:因为@ErwinBrandstetter 注意到上面的这些函数就像所谓的UPSERT(如果存在则更新字段,如果它不存在则插入).这是一个变体,只有UPDATE:

CREATE OR REPLACE FUNCTION "json_object_update_key"(
  "json"          json,
  "key_to_set"    TEXT,
  "value_to_set"  anyelement
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE
  WHEN ("json" -> "key_to_set") IS NULL THEN "json"
  ELSE (SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')
          FROM (SELECT *
                  FROM json_each("json")
                 WHERE "key" <> "key_to_set"
                 UNION ALL
                SELECT "key_to_set", to_json("value_to_set")) AS "fields")::json
END
$function$;
Run Code Online (Sandbox Code Playgroud)

编辑3:这是递归变体,它可以设置(UPSERT)一个叶子值(并使用来自这个答案的第一个函数),位于一个键路径(其中键只能引用内部对象,不支持内部数组):

CREATE OR REPLACE FUNCTION "json_object_set_path"(
  "json"          json,
  "key_path"      TEXT[],
  "value_to_set"  anyelement
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE COALESCE(array_length("key_path", 1), 0)
         WHEN 0 THEN to_json("value_to_set")
         WHEN 1 THEN "json_object_set_key"("json", "key_path"[l], "value_to_set")
         ELSE "json_object_set_key"(
           "json",
           "key_path"[l],
           "json_object_set_path"(
             COALESCE(NULLIF(("json" -> "key_path"[l])::text, 'null'), '{}')::json,
             "key_path"[l+1:u],
             "value_to_set"
           )
         )
       END
  FROM array_lower("key_path", 1) l,
       array_upper("key_path", 1) u
$function$;
Run Code Online (Sandbox Code Playgroud)

更新:功能现在已经压缩.

  • 我想你的PLPGSQL功能,但我不知道如何使用它 - 我看到了一个错误,当我尝试'选择json_object_set_key((从测试选择数据,其中数据 - >>"B" ="2"),"B", 'two');`错误信息是`错误:无法确定多态类型,因为输入的类型为"unknown" (5认同)
  • @ErwinBrandstetter这是真的,但在json中,UPSERT *通常*比类似UPDATE的修改更通用(考虑f.ex。http://sqlfiddle.com/#!15/d41d8/2897)——我解释了原始内容问题是*如何使用 UPDATE 语句修改它们(json 列)?* - 除此之外,单个条件可以将其转换为 UPDATE。 (2认同)

Teo*_*ing 77

使用9.5使用jsonb_set-

UPDATE objects
SET body = jsonb_set(body, '{name}', '"Mary"', true)
WHERE id = 1; 
Run Code Online (Sandbox Code Playgroud)

body是jsonb列类型.

  • @Andrew,您可以使用 `to_jsonb(substring(column_name, 1, 2))` 将列值转换为 jsonb。因此,“更新对象集 body=jsonb_set(body, '{name}', to_jsonb(upper(name_column)), true);”。 (2认同)

Fan*_*nto 43

使用Postgresql 9.5,可以通过以下方式完成 -

UPDATE test
SET data = data - 'a' || '{"a":5}'
WHERE data->>'b' = '2';
Run Code Online (Sandbox Code Playgroud)

要么

UPDATE test
SET data = jsonb_set(data, '{a}', '5'::jsonb);
Run Code Online (Sandbox Code Playgroud)

有人问过如何一次更新jsonb值中的多个字段.假设我们创建一个表:

CREATE TABLE testjsonb ( id SERIAL PRIMARY KEY, object JSONB );
Run Code Online (Sandbox Code Playgroud)

然后我们插入一个实验行:

INSERT INTO testjsonb
VALUES (DEFAULT, '{"a":"one", "b":"two", "c":{"c1":"see1","c2":"see2","c3":"see3"}}');
Run Code Online (Sandbox Code Playgroud)

然后我们更新行:

UPDATE testjsonb SET object = object - 'b' || '{"a":1,"d":4}';
Run Code Online (Sandbox Code Playgroud)

具体如下:

  1. 更新a字段
  2. 删除b字段
  3. 添加d字段

选择数据:

SELECT jsonb_pretty(object) FROM testjsonb;
Run Code Online (Sandbox Code Playgroud)

将导致:

      jsonb_pretty
-------------------------
 {                      +
     "a": 1,            +
     "c": {             +
         "c1": "see1",  +
         "c2": "see2",  +
         "c3": "see3",  +
     },                 +
     "d": 4             +
 }
(1 row)
Run Code Online (Sandbox Code Playgroud)

要更新内部字段,请不要使用concat运算符||.请改用jsonb_set.这不简单:

UPDATE testjsonb SET object =
jsonb_set(jsonb_set(object, '{c,c1}','"seeme"'),'{c,c2}','"seehim"');
Run Code Online (Sandbox Code Playgroud)

例如,使用{c,c1}的concat运算符:

UPDATE testjsonb SET object = object || '{"c":{"c1":"seedoctor"}}';
Run Code Online (Sandbox Code Playgroud)

将删除{c,c2}和{c,c3}.

要获得更多功能,请在postgresql json函数文档中寻求功能.人们可能对#-操作员,jsonb_set功能和jsonb_insert功能感兴趣.


Kou*_*rev 21

从 PostgreSQL 14 开始,您可以使用jsonb 下标直接访问 JSON 字段的元素并最终更新它们。

UPDATE test SET data['a'] = '5' WHERE data['b'] = '2';
Run Code Online (Sandbox Code Playgroud)


Nee*_*thu 14

如果您的字段类型是 json,则以下内容对您有用。

UPDATE 
table_name
SET field_name = field_name::jsonb - 'key' || '{"key":new_val}' 
WHERE field_name->>'key' = 'old_value'.
Run Code Online (Sandbox Code Playgroud)

运算符“-”从左操作数中删除键/值对或字符串元素。键/值对根据其键值进行匹配。

运算符“||” 将两个 jsonb 值连接成一个新的 jsonb 值。

由于这些是 jsonb 运算符,因此您只需将类型转换为 ::jsonb

更多信息:JSON 函数和运算符

你可以在这里阅读我的笔记


Dro*_*ror 12

我发现以前的答案更适合有经验的 PostgreSQL 用户。这是针对初学者的:

假设您有一个 JSONB 类型的表列,其值如下:

{
    "key0": {
        "key01": "2018-05-06T12:36:11.916761+00:00",
        "key02": "DEFAULT_WEB_CONFIGURATION",
        
    "key1": {
        "key11": "Data System",
        "key12": "<p>Health,<p>my address<p>USA",
        "key13": "*Please refer to main screen labeling"
    }
}
Run Code Online (Sandbox Code Playgroud)

假设我们要在该行中设置一个新值:

"key13": "*Please refer to main screen labeling"
Run Code Online (Sandbox Code Playgroud)

并改为放置值:

"key13": "See main screen labeling"
Run Code Online (Sandbox Code Playgroud)

我们使用 json_set() 函数为 key13 分配一个新值

jsonb_set()的参数

jsonb_set(target jsonb, path text[], new_value jsonb[, create_missing boolean])
Run Code Online (Sandbox Code Playgroud)

在“ target ”中 - 我将放置 jsonb 列名称(这是正在修改的表列)

path ” - 是通向(并包括)我们要覆盖的密钥的“json 键路径”

new_value ” - 这是我们分配的新值

在我们的例子中,我们想要更新位于 key1 下的 key13 的值 ( key1 -> key13 ) :

因此路径语法是:'{key1,key13}'(路径是最难破解的部分 - 因为教程很糟糕)

jsonb_set(jsonb_column,'{key1,key13}','"See main screen labeling"')
Run Code Online (Sandbox Code Playgroud)


shr*_*hru 9

为了构建@ pozs的答案,这里有一些PostgreSQL函数可能对某些人有用.(需要PostgreSQL 9.3+)

按键删除:按键从JSON结构中删除值.

CREATE OR REPLACE FUNCTION "json_object_del_key"(
  "json"          json,
  "key_to_del"    TEXT
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE
  WHEN ("json" -> "key_to_del") IS NULL THEN "json"
  ELSE (SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')
          FROM (SELECT *
                  FROM json_each("json")
                 WHERE "key" <> "key_to_del"
               ) AS "fields")::json
END
$function$;
Run Code Online (Sandbox Code Playgroud)

按键递归删除:按键路径从JSON结构中删除值.(需要@ pozs的json_object_set_key功能)

CREATE OR REPLACE FUNCTION "json_object_del_path"(
  "json"          json,
  "key_path"      TEXT[]
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE
  WHEN ("json" -> "key_path"[l] ) IS NULL THEN "json"
  ELSE
     CASE COALESCE(array_length("key_path", 1), 0)
         WHEN 0 THEN "json"
         WHEN 1 THEN "json_object_del_key"("json", "key_path"[l])
         ELSE "json_object_set_key"(
           "json",
           "key_path"[l],
           "json_object_del_path"(
             COALESCE(NULLIF(("json" -> "key_path"[l])::text, 'null'), '{}')::json,
             "key_path"[l+1:u]
           )
         )
       END
    END
  FROM array_lower("key_path", 1) l,
       array_upper("key_path", 1) u
$function$;
Run Code Online (Sandbox Code Playgroud)

用法示例:

s1=# SELECT json_object_del_key ('{"hello":[7,3,1],"foo":{"mofu":"fuwa", "moe":"kyun"}}',
                                 'foo'),
            json_object_del_path('{"hello":[7,3,1],"foo":{"mofu":"fuwa", "moe":"kyun"}}',
                                 '{"foo","moe"}');

 json_object_del_key |          json_object_del_path
---------------------+-----------------------------------------
 {"hello":[7,3,1]}   | {"hello":[7,3,1],"foo":{"mofu":"fuwa"}}
Run Code Online (Sandbox Code Playgroud)


sig*_*god 9

UPDATE test
SET data = data::jsonb - 'a' || '{"a":5}'::jsonb
WHERE data->>'b' = '2'
Run Code Online (Sandbox Code Playgroud)

这似乎适用于PostgreSQL 9.5

  • 据我所知,这对我有用,这会从数据中删除字段“a”,然后在字段“a”中附加新值。就我而言,“a”的值基于列。更新测试集数据 = data::jsonb - 'a' || ('{"a":"'|| myColumn || '"}')::jsonb; (2认同)

小智 7

您可以尝试如下更新:

语法: UPDATE table_name SET column_name = column_name::jsonb || '{"key":new_value}' WHERE column_name 条件;

对于您的示例:

更新测试集数据 = 数据::jsonb || '{"a":new_value}' WHERE data->>'b' = '2';


Ant*_*nio 6

在尝试更新字符串类型字段时,这对我有用。

UPDATE table_name 
SET body = jsonb_set(body, '{some_key}', to_json('value'::text)::jsonb);
Run Code Online (Sandbox Code Playgroud)

希望它可以帮助别人!

假设表 table_name 有一个名为 body 的 jsonb 列,并且您想更改 body.some_key = 'value'