97

我需要从 json 类型列中删除一些属性。

桌子:

CREATE TABLE my_table( id VARCHAR(80), data json);
INSERT INTO my_table (id, data) VALUES (
  'A', 
  '{"attrA":1,"attrB":true,"attrC":["a", "b", "c"]}'
);

现在,我需要attrB从 column中删除data

类似的东西alter table my_table drop column data->'attrB';会很好。但是使用临时表的方法也足够了。

4

11 回答 11

142

更新:对于 9.5+,您可以使用显式运算符jsonb(如果您有json类型化列,则可以使用强制转换来应用修改):

可以使用操作符从 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]'

可以使用#-操作符从 JSON 层次结构的深处删除:

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

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

相关:PostgreSQL 中的其他 JSON 操作:

原始答案(适用于 PostgreSQL 9.3):

如果您至少有 PostgreSQL 9.3,您可以将对象拆分成对json_each()并过滤不需要的字段,然后再次手动构建 json。就像是:

SELECT data::text::json AS before,
       ('{' || array_to_string(array_agg(to_json(l.key) || ':' || l.value), ',') || '}')::json AS after
FROM (VALUES ('{"attrA":1,"attrB":true,"attrC":["a","b","c"]}'::json)) AS v(data),
LATERAL (SELECT * FROM json_each(data) WHERE "key" <> 'attrB') AS l
GROUP BY data::text

对于 9.2(或更低),这是不可能的。

编辑

更方便的形式是创建一个函数,它可以删除字段中任意数量的属性json

编辑2string_agg()比便宜array_to_string(array_agg())

CREATE OR REPLACE FUNCTION "json_object_delete_keys"("json" json, VARIADIC "keys_to_delete" TEXT[])
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT COALESCE(
  (SELECT ('{' || string_agg(to_json("key") || ':' || "value", ',') || '}')
   FROM json_each("json")
   WHERE "key" <> ALL ("keys_to_delete")),
  '{}'
)::json
$function$;

使用此功能,您需要做的就是运行以下查询:

UPDATE my_table
SET data = json_object_delete_keys(data, 'attrB');
于 2014-05-06T09:56:08.180 回答
55

使用 JSONB 类型的 PostgreSQL 9.5 使这变得容易得多。请参阅此处记录的 JSONB 运算符。

您可以使用“-”运算符删除顶级属性。

SELECT '{"a": {"key":"value"}, "b": 2, "c": true}'::jsonb - 'a'
// -> {"b": 2, "c": true}

您可以在更新调用中使用它来更新现有的 JSONB 字段。

UPDATE my_table SET data = data - 'attrB'

如果在函数中使用,您还可以通过参数动态提供属性名称。

CREATE OR REPLACE FUNCTION delete_mytable_data_key(
    _id integer,
    _key character varying)
  RETURNS void AS
$BODY$
BEGIN
    UPDATE my_table SET
        data = data - _key
    WHERE id = _id;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

反向运算符是“||”,以便将两个 JSONB 数据包连接在一起。请注意,该属性的最右侧使用将覆盖任何以前的。

SELECT '{"a": true, "c": true}'::jsonb || '{"a": false, "b": 2}'::jsonb 
// -> {"a": false, "b": 2, "c": true}
于 2016-02-09T03:49:06.137 回答
6

我无法SELECT '{"a": "b"}'::jsonb - 'a';在 9.5.2 中工作。然而,SELECT '{"a": "b"}'::jsonb #- '{a}';确实奏效了!

于 2017-02-06T22:11:28.930 回答
6

我在从 postgres 中现有的 json 列中删除键值对时遇到了类似的问题。我能够使用-运算符解决此问题,如下所示:

UPDATE my_table
SET data = data::jsonb - 'attrB'
WHERE id = 'A';
于 2021-10-19T12:14:08.227 回答
4

只需使用#-运算符,例如:

SELECT '{"foo": 10, "bar": [], "baz": {}}'::jsonb #- '{baz}';
于 2020-09-17T16:48:05.847 回答
2

这是一个丑陋的黑客,但如果attrB不是您的第一个密钥并且它只出现一次,那么您可以执行以下操作:

UPDATE my_table SET data = REPLACE(data::text, ',"attrB":' || (data->'attrB')::text, '')::json;
于 2015-04-28T17:49:43.140 回答
2

虽然这在 9.5+ 中使用 jsonb 运算符肯定更容易,但 pozs 编写的删除多个键的函数仍然有用。例如,如果要删除的键存储在表中,您可以使用该函数将它们全部删除。这是一个更新的函数,使用 jsonb 和 postgresql 9.5+:

CREATE FUNCTION remove_multiple_keys(IN object jsonb, 
                                     variadic keys_to_delete text[], 
                                     OUT jsonb)
  IMMUTABLE
  STRICT
  LANGUAGE SQL
AS 
$$
  SELECT jsonb_object_agg(key, value)
     FROM (SELECT key, value 
           FROM jsonb_each("object")
           WHERE NOT (key = ANY("keys_to_delete")) 
     ) each_subselect
$$
;

如果要删除的键存储在表中(例如,在表“table_with_keys”的“键”列中),您可以这样调用此函数:

SELECT remove_multiple_keys(my_json_object, 
                            VARIADIC (SELECT array_agg(keys) FROM table_with_keys));
于 2018-02-09T21:11:03.400 回答
1

另一种方便的方法是使用 hstore 扩展。通过这种方式,您可以编写一些更方便的函数来将键设置/删除到 json 对象中。我想出了以下功能来做同样的事情:

CREATE OR REPLACE FUNCTION remove_key(json_in json, key_name text)
 RETURNS json AS $$
 DECLARE item json;
 DECLARE fields hstore;
BEGIN
 -- Initialize the hstore with desired key being set to NULL
 fields := hstore(key_name,NULL);

 -- Parse through Input Json and push each key into hstore 
 FOR item IN  SELECT row_to_json(r.*) FROM json_each_text(json_in) AS r
 LOOP
   --RAISE NOTICE 'Parsing Item % %', item->>'key', item->>'value';
   fields := (fields::hstore || hstore(item->>'key', item->>'value'));
 END LOOP;
 --RAISE NOTICE 'Result %', hstore_to_json(fields);
 -- Remove the desired key from store
 fields := fields-key_name;

 RETURN hstore_to_json(fields);
END;
$$ LANGUAGE plpgsql
SECURITY DEFINER
STRICT;

一个简单的使用示例是:

SELECT remove_key(('{"Name":"My Name", "Items" :[{ "Id" : 1, "Name" : "Name 1"}, { "Id" : 2, "Name 2" : "Item2 Name"}]}')::json, 'Name');
-- Result
"{"Items": "[{ \"Id\" : 1, \"Name\" : \"Name 1\"}, { \"Id\" : 2, \"Name 2\" : \"Item2 Name\"}]"}"

我有另一个函数来执行 set_key 操作以及以下操作:

CREATE OR REPLACE FUNCTION set_key(json_in json, key_name text, key_value text)
RETURNS json AS $$
DECLARE item json;
DECLARE fields hstore;
BEGIN
 -- Initialize the hstore with desired key value
 fields := hstore(key_name,key_value);

 -- Parse through Input Json and push each key into hstore 
 FOR item IN  SELECT row_to_json(r.*) FROM json_each_text(json_in) AS r
 LOOP
   --RAISE NOTICE 'Parsing Item % %', item->>'key', item->>'value';
   fields := (fields::hstore || hstore(item->>'key', item->>'value'));
 END LOOP;
 --RAISE NOTICE 'Result %', hstore_to_json(fields);
 RETURN hstore_to_json(fields);
END;
$$ LANGUAGE plpgsql
SECURITY DEFINER
STRICT;

我在这里的博客中对此进行了更多讨论。

于 2014-09-14T21:00:42.063 回答
1

我一直在努力寻找一个简单的更新查询,它在 Postgres 9.4 中删除了 json 键而不创建函数,所以这是对@posz 答案的更新。

UPDATE someTable t
SET someField = newValue
FROM (
    SELECT id,
        json_object_agg(l.key, l.value)::text AS newValue
    FROM someTable t,
        LATERAL (
            SELECT *
            FROM json_each(t.someField::json)
            WHERE "key" <> ALL (ARRAY['key1', 'key2', 'key3'])
        ) AS l
    GROUP BY id
) upd
WHERE t.id = upd.id

查询假设您有一个这样的表:

CREATE TABLE myTable (
    id SERIAL PRIMARY KEY,
    someField text
);

我想您可以使用@posz answer 中的这一行而不是 json_object_agg 行,使其适用于较旧的 postgres,但我没有对其进行测试。

('{' || array_to_string(array_agg(to_json(l.key) || ':' || l.value), ',') || '}')::json AS after

还要确保从子请求中运行选择以确保您正在更新正确的数据

于 2020-05-20T11:57:16.040 回答
0

如果您不仅要从单个文档中删除 JSON 属性,还要从数组中删除 JSON 属性,请考虑使用如下语句:

    UPDATE table_to_update t
    SET jsonColumnWithArray = (SELECT JSON_AGG(jsonItem)
               FROM (SELECT JSON_ARRAY_ELEMENTS(t1.jsonColumnWithArray)::JSONB #- '{customerChange, oldPeriod}' AS jsonItem
                     FROM table_to_update t1
                     WHERE t1.id = t1.id) AS t2);

输入

[
  {
    "customerChange": {
      "newPeriod": null,
      "oldPeriod": null
    }
  },
  {
    "customerChange": {
      "newPeriod": null,
      "oldPeriod": null
    }
  }
]

输出

[
  {
    "customerChange": {
      "newPeriod": null
    }
  },
  {
    "customerChange": {
      "newPeriod": null
    }
  }
]
于 2021-01-22T15:23:56.110 回答
0

如果要删除子字段,例如:

{
  "a": {
    "b": "REMOVE ME!"
  }
}

您可以简单地使用:

UPDATE my_table
SET my_json_column = my_json_column::jsonb #- '{a,b}';
于 2021-12-09T10:26:55.447 回答