web-dev-qa-db-ja.com

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'; いいだろう。しかし、一時テーブルを使用する方法でも十分です。

45
sja

Update: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_object_agg()を使用してjsonオブジェクトに直接集約できます。

関連: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フィールドの任意の数の属性を削除できる関数を作成することです。

Edit 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');
_
66
pozs

JSONBタイプを使用するPostgreSQL 9.5では、これがはるかに簡単になりました。ドキュメント化されたJSONB演算子 here を参照してください。

「-」演算子を使用して、最上位の属性を削除できます。

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;

2つのJSONBパケットを連結するための逆演算子は「||」です。属性を右端で使用すると、以前の属性が上書きされることに注意してください。

SELECT '{"a": true, "c": true}'::jsonb || '{"a": false, "b": 2}'::jsonb 
// -> {"a": false, "b": 2, "c": true}
38
mujimu

これはugいハックですが、attrBが最初のキーではなく、一度しか表示されない場合は、次のことができます。

UPDATE my_table SET data = REPLACE(data::text, ',"attrB":' || (data->'attrB')::text, '')::json;
2
KARASZI István

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"の列 "keys"に)、次のようにこの関数を呼び出すことができます。

SELECT remove_multiple_keys(my_json_object, 
                            VARIADIC (SELECT array_agg(keys) FROM table_with_keys));
2
Jeremy

SELECT '{"a": "b"}'::jsonb - 'a'; 9.5.2で動作します。しかしながら、 SELECT '{"a": "b"}'::jsonb #- '{a}';うまくいきました!

2
Westy92

これを行うもう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;

これについては ブログ で詳しく説明しています。

1
Sumit Chawla