PostgreSQL: удалить атрибут из столбца JSON

мне нужно удалить некоторые атрибуты из столбца типа 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 из колонки data.

что-то вроде alter table my_table drop column data->'attrB'; было бы неплохо. Но и способа с временным столом было бы достаточно.

6 ответов


обновление: для 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().

связанные: другие манипуляции JSON с PostgreSQL:

оригинальный ответ (относится к 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 (или ниже) это не возможно.

редактировать:

более удобной формой является создание функции, которая может удалить любое количество атрибутов в


это стало намного проще с PostgreSQL 9.5 с использованием типа JSONB. См. документированные операторы 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}

это уродливый хак, но если attrB это не ваш первый ключ, и он появляется только один раз, тогда вы можете сделать следующее:

UPDATE my_table SET data = REPLACE(data::text, ',"attrB":' || (data->'attrB')::text, '')::json;

Я не мог получить SELECT '{"a": "b"}'::jsonb - 'a'; для работы в 9.5.2. Однако,SELECT '{"a": "b"}'::jsonb #- '{a}'; сработало!


один удобный способ сделать это-использовать расширение 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;

Я обсудил это больше в моем блог здесь.


хотя это, безусловно, проще в 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));