J'ai besoin de supprimer certains attributs d'une colonne de type JSON.
La table:
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"]}'
);
Maintenant, je dois supprimer attrB
de la colonne data
.
Quelque chose comme alter table my_table drop column data->'attrB';
serait bien. Mais un moyen avec une table temporaire serait également suffisant.
Update: pour la version 9.5+, il existe des opérateurs explicites que vous pouvez utiliser avec jsonb
(si vous avez une colonne de type json
, vous pouvez utiliser des conversions pour appliquer une modification):
La suppression d'une clé (ou d'un index) d'un objet JSON (ou d'un tableau) peut être effectuée à l'aide de l'opérateur -
:
SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
jsonb '["a",1,"b",2]' - 1 -- will yield jsonb '["a","b",2]'
La suppression, au plus profond d'une hiérarchie JSON, peut être effectuée à l'aide de l'opérateur #-
:
SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
-- will yield jsonb '{"a":[null,{"b":[]}]}'
Pour 9.4, vous pouvez utiliser une version modifiée de la réponse d'origine (ci-dessous), mais au lieu d'agréger une chaîne JSON, vous pouvez l'agréger directement dans un objet json
avec json_object_agg()
.
Connexe: autres manipulations JSON dans PostgreSQL:
Réponse originale (s'applique à PostgreSQL 9.3):
Si vous avez au moins PostgreSQL 9.3, vous pouvez scinder votre objet en paires avec json_each()
et filtrer vos champs non désirés, puis reconstituer manuellement le json. Quelque chose comme:
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
Avec 9.2 (ou moins), ce n'est pas possible.
Modifier:
Une forme plus pratique consiste à créer une fonction pouvant supprimer un nombre quelconque d'attributs dans un champ json
:
Edit 2: string_agg()
est moins cher que 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$;
Avec cette fonction, tout ce que vous avez à faire est d’exécuter la requête ci-dessous:
UPDATE my_table
SET data = json_object_delete_keys(data, 'attrB');
Cela est devenu beaucoup plus facile avec PostgreSQL 9.5 en utilisant le type JSONB. Voir les opérateurs JSONB documentés ici .
Vous pouvez supprimer un attribut de niveau supérieur avec l'opérateur "-".
SELECT '{"a": {"key":"value"}, "b": 2, "c": true}'::jsonb - 'a'
// -> {"b": 2, "c": true}
Vous pouvez l'utiliser dans un appel de mise à jour pour mettre à jour un champ JSONB existant.
UPDATE my_table SET data = data - 'attrB'
Vous pouvez également fournir le nom de l'attribut de manière dynamique via un paramètre s'il est utilisé dans une fonction.
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;
L'opérateur inverse est le "||", afin de concaténer deux paquets JSONB. Notez que l'utilisation la plus à droite de l'attribut écrasera toutes les précédentes.
SELECT '{"a": true, "c": true}'::jsonb || '{"a": false, "b": 2}'::jsonb
// -> {"a": false, "b": 2, "c": true}
C'est un hack moche, mais si attrB
n'est pas votre première clé et qu'elle n'apparaît qu'une fois, vous pouvez alors:
UPDATE my_table SET data = REPLACE(data::text, ',"attrB":' || (data->'attrB')::text, '')::json;
Je ne pouvais pas obtenir SELECT '{"a": "b"}'::jsonb - 'a';
dans 9.5.2. Cependant, SELECT '{"a": "b"}'::jsonb #- '{a}';
a fonctionné!
Un autre moyen pratique consiste à utiliser l'extension hstore. De cette façon, vous pouvez écrire une fonction plus pratique pour définir/supprimer des clés dans un objet json. Je suis venu avec la fonction suivante pour faire la même chose:
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;
Un exemple simple d'utilisation est:
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\"}]"}"
J'ai une autre fonction pour effectuer l'opération set_key ainsi que les suivantes:
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;
J'ai discuté de cela plus dans mon blog ici.
Bien que cela soit certainement plus facile avec les opérateurs jsonb dans la version 9.5+, la fonction que pozs a écrite pour supprimer plusieurs clés est toujours utile. Par exemple, si les clés à supprimer sont stockées dans une table, vous pouvez utiliser la fonction pour toutes les supprimer. Voici une fonction mise à jour, utilisant jsonb et 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
$$
;
Si les clés à supprimer sont stockées dans une table (par exemple, dans la colonne "clés" de la table "table_avec_codes"), vous pouvez appeler cette fonction de la manière suivante:
SELECT remove_multiple_keys(my_json_object,
VARIADIC (SELECT array_agg(keys) FROM table_with_keys));