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

使用 postgresql 9.3 我可以 SELECT 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...

最佳答案

更新:With PostgreSQL 9.5 ,有一些jsonb PostgreSQL 本身内的操作功能(但 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]'

所以,设置一个简单的键可以使用:

SELECT jsonb '{"a":1}' || jsonb_build_object('<key>', '<value>')

在哪里 <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}]}]}'

jsonb_set() 的完整参数列表:

jsonb_set(target         jsonb,
          path           text[],
          new_value      jsonb,
          create_missing boolean default true)

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]}]}'

对于插入 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]}]}'

jsonb_insert() 的完整参数列表:

jsonb_insert(target       jsonb,
             path         text[],
             new_value    jsonb,
             insert_after boolean default false)

同样,path 中出现的负整数从 JSON 数组的末尾开始计数。

所以,前任。可以通过以下方式追加到 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

但是,当 jsonb_set()path是 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

从 JSON 对象(或数组)中删除键(或索引)可以使用 target运营商:

SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
       jsonb '["a",1,"b",2]' - 1    -- will yield jsonb '["a","b",2]'

从 JSON 层次结构的深处删除可以使用 - 来完成运营商:

SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
-- will yield jsonb '{"a":[null,{"b":[]}]}'

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

原答案:在纯 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$;

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$;

编辑 2:作为@ErwinBrandstetter noted上面的这些函数就像所谓的 json_object_agg() (如果存在则更新字段,如果不存在则插入)。这是一个变体,只有 UPSERT :

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$;

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

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$;

更新:添加了用另一个给定键替换现有 json 字段键的功能。可以方便地在迁移或数据结构修改等其他场景中更新数据类型。

CREATE OR REPLACE FUNCTION json_object_replace_key(
    json_value json,
    existing_key text,
    desired_key text)
  RETURNS json AS
$BODY$
SELECT COALESCE(
(
    SELECT ('{' || string_agg(to_json(key) || ':' || value, ',') || '}')
    FROM (
        SELECT *
        FROM json_each(json_value)
        WHERE key <> existing_key
        UNION ALL
        SELECT desired_key, json_value -> existing_key
    ) AS "fields"
    -- WHERE value IS NOT NULL (Actually not required as the string_agg with value's being null will "discard" that entry)

),
    '{}'
)::json
$BODY$
  LANGUAGE sql IMMUTABLE STRICT
  COST 100;

更新:函数现在被压缩了。

https://stackoverflow.com/questions/18209625/

相关文章:

jquery - 将数组传递给 $.ajax() 中的 ajax 请求

ruby - 'json' 原生 gem 需要安装构建工具

javascript - 打印 JSON 解析的对象?

c# - 在 C# 中读取和解析 Json 文件

jquery - 如何在没有表单的情况下将字符串数组发布到 ASP.NET MVC Controll

php - 如何使用 PHP 从 JSON 中提取数据?

jquery - 如何使用 jQuery/JavaScript 解析 JSON 数据?

ajax - 将 .ajax() 与 JSONP 一起使用的基本示例?

java - Java 中的 pretty-print JSON

json - 如何在构建 JSON 字符串时转义特殊字符?