Comment modifier les champs à l'intérieur du nouveau type de données JSON PostgreSQL?

236

Avec postgresql 9.3, je peux sélectionner des champs spécifiques d'un type de données JSON, mais comment les modifiez-vous en utilisant UPDATE? Je ne trouve aucun exemple de cela dans la documentation postgresql, ou n'importe où en ligne. J'ai essayé l'évidence:

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...
user9645
la source

Réponses:

332

Mise à jour : Avec PostgreSQL 9.5 , il existe des jsonbfonctionnalités de manipulation dans PostgreSQL lui-même (mais aucune pour json; les transtypages sont requis pour manipuler les jsonvaleurs).

Fusion de 2 (ou plusieurs) objets JSON (ou concaténation de tableaux):

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

Ainsi, la définition d'une clé simple peut être effectuée en utilisant:

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

<key>doit être une chaîne et <value>peut être n'importe quel type to_jsonb()accepté.

Pour définir une valeur profondément dans une hiérarchie JSON , la jsonb_set()fonction peut être utilisée:

SELECT jsonb_set('{"a":[null,{"b":[]}]}', '{a,1,b,0}', jsonb '{"c":3}')
-- will yield jsonb '{"a":[null,{"b":[{"c":3}]}]}'

Liste complète des paramètres de jsonb_set():

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

pathpeut également contenir des index de tableaux JSON et des entiers négatifs qui apparaissent à partir de la fin des tableaux JSON. Cependant, un index de tableau JSON inexistant mais positif ajoutera l'élément à la fin du tableau:

SELECT jsonb_set('{"a":[null,{"b":[1,2]}]}', '{a,1,b,1000}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}'

Pour l' insertion dans le tableau JSON (tout en préservant toutes les valeurs d'origine) , la jsonb_insert()fonction peut être utilisée ( en 9.6+; cette fonction uniquement, dans cette section ):

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

Liste complète des paramètres de jsonb_insert():

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

Encore une fois, des nombres entiers négatifs qui apparaissent en pathnombre à partir de la fin des tableaux JSON.

Donc, ex. l'ajout à la fin d'un tableau JSON peut se faire avec:

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

Cependant, cette fonction fonctionne un peu différemment (que jsonb_set()) lorsque l' pathen targetest la clé d'un objet JSON. Dans ce cas, il n'ajoutera une nouvelle paire clé-valeur pour l'objet JSON que lorsque la clé n'est pas utilisée. S'il est utilisé, il générera une erreur:

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

La suppression d'une clé (ou d'un index) d'un objet JSON (ou d'un tableau) peut se faire avec 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 se faire avec 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().

Réponse originale : Il est possible (sans plpython ou plv8) également en SQL pur (mais nécessite 9.3+, ne fonctionnera pas avec 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

Modifier :

Une version, qui définit plusieurs clés et valeurs:

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

Edit 2 : comme @ErwinBrandstetter l'a noté, ces fonctions ci-dessus fonctionnent comme un soi-disant UPSERT(met à jour un champ s'il existe, insère s'il n'existe pas). Voici une variante, qui seulement UPDATE:

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

Edit 3 : Voici une variante récursive, qui peut définir ( UPSERT) une valeur de feuille (et utilise la première fonction de cette réponse), située sur un chemin de clé (où les clés ne peuvent se référer qu'à des objets internes, des tableaux internes non pris en charge):

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

Mise à jour : les fonctions sont maintenant compactées.

pozs
la source
5
J'ai essayé votre fonction plpgsql, mais je ne sais pas comment l'utiliser - je vois une erreur lorsque j'essaie le select json_object_set_key((select data from test where data->>'b' = '2'), 'b', 'two'); message d'erreur est ERROR: could not determine polymorphic type because input has type "unknown"
user9645
1
Cela effectue l'équivalent d'un UPSERT, pas d'un UPDATE. Si la clé n'existe pas encore dans le champ json, elle est ajoutée. Regardez cette question connexe pour un réel UPDATE: stackoverflow.com/questions/7711432/… (Ceci est pour un type composite, mais le principal est similaire pour json.)
Erwin Brandstetter
1
@ErwinBrandstetter c'est vrai, mais dans json un UPSERT est généralement plus général qu'une modification de type UPDATE (pensez à f.ex. sqlfiddle.com/#!15/d41d8/2897 ) - J'ai interprété la question d'origine comme comment modifier les (colonnes json) à l'aide d'une instruction UPDATE? - en plus une seule condition pourrait transformer cela en UPDATE.
pozs
1
Très utile et complet maintenant.
Erwin Brandstetter
1
@maxhud qui dépend du client (ou de la bibliothèque cliente que vous utilisez). Si vous le pouvez, utilisez des types explicites (PostgreSQL peut deviner les types dans les requêtes paramétrées, mais cela ne fonctionne généralement pas bien avec les fonctions polymorphes). Mais au moins, vous pouvez utiliser des transtypages explicites, comme $2::text.
pozs
98

Avec 9.5, utilisez jsonb_set-

UPDATE objects
SET body = jsonb_set(body, '{name}', '"Mary"', true)
WHERE id = 1; 

où body est un type de colonne jsonb.

Teo Choong Ping
la source
Salut, pourquoi je ne peux pas utiliser uppercomme ça: update objects set body=jsonb_set(body, '{name}', upper('"Mary"'), true) where id=1;il ne reconnaît pas, ou comment puis-je obtenir le même comportement? thx
Rafael Capucho
1
Si la valeur que je veux définir est une sous-chaîne d'une autre colonne plutôt que "Mary", comment dois-je procéder?
Andrew
58

Avec Postgresql 9.5, cela peut être fait en suivant-

UPDATE test
SET data = data - 'a' || '{"a":5}'
WHERE data->>'b' = '2';

OU

UPDATE test
SET data = jsonb_set(data, '{a}', '5'::jsonb);

Quelqu'un a demandé comment mettre à jour simultanément plusieurs champs de la valeur jsonb. Supposons que nous créons une table:

CREATE TABLE testjsonb ( id SERIAL PRIMARY KEY, object JSONB );

Ensuite, nous INSÉRONS une ligne expérimentale:

INSERT INTO testjsonb
VALUES (DEFAULT, '{"a":"one", "b":"two", "c":{"c1":"see1","c2":"see2","c3":"see3"}}');

Ensuite, nous METTONS À JOUR la ligne:

UPDATE testjsonb SET object = object - 'b' || '{"a":1,"d":4}';

Ce qui fait ce qui suit:

  1. Met à jour le champ a
  2. Supprime le champ b
  3. Ajoutez le champ d

Sélection des données:

SELECT jsonb_pretty(object) FROM testjsonb;

Aura pour résultat:

      jsonb_pretty
-------------------------
 {                      +
     "a": 1,            +
     "c": {             +
         "c1": "see1",  +
         "c2": "see2",  +
         "c3": "see3",  +
     },                 +
     "d": 4             +
 }
(1 row)

Pour mettre à jour le champ à l'intérieur, n'utilisez pas l'opérateur concat ||. Utilisez jsonb_set à la place. Ce qui n'est pas simple:

UPDATE testjsonb SET object =
jsonb_set(jsonb_set(object, '{c,c1}','"seeme"'),'{c,c2}','"seehim"');

Utiliser l'opérateur concat pour {c, c1} par exemple:

UPDATE testjsonb SET object = object || '{"c":{"c1":"seedoctor"}}';

Supprime {c, c2} et {c, c3}.

Pour plus de puissance, recherchez la puissance dans la documentation des fonctions json postgresql . On pourrait être intéressé par l' #-opérateur, la jsonb_setfonction et aussi la jsonb_insertfonction.

Fandi Susanto
la source
et si je dois mettre à jour deux champs, quelle est la syntaxe?
Sunil Garg
si j'ai une colonne json avec le nom du champ, comment puis-je ajouter un champ de nom de famille à cette colonne
Bionix1441
Doit être clair:UPDATE users SET profile = profile || '{"lastname":"Washington"}' WHERE profile->>'name' = 'George Washington';
Fandi Susanto
9

Pour s'appuyer sur les réponses de @ pozs, voici quelques fonctions PostgreSQL supplémentaires qui peuvent être utiles à certains. (Nécessite PostgreSQL 9.3+)

Supprimer par clé: supprime une valeur de la structure JSON par clé.

CREATE OR REPLACE FUNCTION "json_object_del_key"(
  "json"          json,
  "key_to_del"    TEXT
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE
  WHEN ("json" -> "key_to_del") IS NULL THEN "json"
  ELSE (SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')
          FROM (SELECT *
                  FROM json_each("json")
                 WHERE "key" <> "key_to_del"
               ) AS "fields")::json
END
$function$;

Suppression récursive par clé: supprime une valeur de la structure JSON par chemin de clé. (nécessite la json_object_set_keyfonction de @ pozs )

CREATE OR REPLACE FUNCTION "json_object_del_path"(
  "json"          json,
  "key_path"      TEXT[]
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE
  WHEN ("json" -> "key_path"[l] ) IS NULL THEN "json"
  ELSE
     CASE COALESCE(array_length("key_path", 1), 0)
         WHEN 0 THEN "json"
         WHEN 1 THEN "json_object_del_key"("json", "key_path"[l])
         ELSE "json_object_set_key"(
           "json",
           "key_path"[l],
           "json_object_del_path"(
             COALESCE(NULLIF(("json" -> "key_path"[l])::text, 'null'), '{}')::json,
             "key_path"[l+1:u]
           )
         )
       END
    END
  FROM array_lower("key_path", 1) l,
       array_upper("key_path", 1) u
$function$;

Exemples d'utilisation:

s1=# SELECT json_object_del_key ('{"hello":[7,3,1],"foo":{"mofu":"fuwa", "moe":"kyun"}}',
                                 'foo'),
            json_object_del_path('{"hello":[7,3,1],"foo":{"mofu":"fuwa", "moe":"kyun"}}',
                                 '{"foo","moe"}');

 json_object_del_key |          json_object_del_path
---------------------+-----------------------------------------
 {"hello":[7,3,1]}   | {"hello":[7,3,1],"foo":{"mofu":"fuwa"}}
shru
la source
Très utile! Je vous remercie.
1111161171159459134 du
9
UPDATE test
SET data = data::jsonb - 'a' || '{"a":5}'::jsonb
WHERE data->>'b' = '2'

Cela semble fonctionner sur PostgreSQL 9.5

Sigod
la source
Fonctionne pour moi, si j'ai bien compris, cela supprime le champ "a" des données, puis ajoute le champ "a" avec la nouvelle valeur. Dans mon cas, la valeur de "a" était basée sur une colonne. UPDATE test SET data = data :: jsonb - 'a' || ('{"a": "' || myColumn || '"}') :: jsonb;
sebge2
7

Si votre type de champ est json, ce qui suit fonctionnera pour vous.

UPDATE 
table_name
SET field_name = field_name::jsonb - 'key' || '{"key":new_val}' 
WHERE field_name->>'key' = 'old_value'.

L'opérateur '-' supprime la paire clé / valeur ou l'élément de chaîne de l'opérande gauche. Les paires clé / valeur sont appariées en fonction de leur valeur clé.

Opérateur '||' concaténer deux valeurs jsonb en une nouvelle valeur jsonb.

Puisque ce sont des opérateurs jsonb, il vous suffit de transtyper en :: jsonb

Plus d'informations: Fonctions et opérateurs JSON

Vous pouvez lire ma note ici

Neethu
la source
Un moyen simple et meilleur de mettre à jour les champs JSON, si vous n'êtes pas préoccupé par les réarrangements de l'ordre des propriétés.
Karthik Sivaraj
4

Avec PostgreSQL 9.4, nous avons implémenté la fonction python suivante. Il peut également fonctionner avec PostgreSQL 9.3.

create language plpython2u;

create or replace function json_set(jdata jsonb, jpaths jsonb, jvalue jsonb) returns jsonb as $$
import json

a = json.loads(jdata)
b = json.loads(jpaths)

if a.__class__.__name__ != 'dict' and a.__class__.__name__ != 'list':
  raise plpy.Error("The json data must be an object or a string.")

if b.__class__.__name__ != 'list':
   raise plpy.Error("The json path must be an array of paths to traverse.")

c = a
for i in range(0, len(b)):
  p = b[i]
  plpy.notice('p == ' + str(p))

  if i == len(b) - 1:
    c[p] = json.loads(jvalue)

  else:
    if p.__class__.__name__ == 'unicode':
      plpy.notice("Traversing '" + p + "'")
      if c.__class__.__name__ != 'dict':
        raise plpy.Error("  The value here is not a dictionary.")
      else:
        c = c[p]

    if p.__class__.__name__ == 'int':
      plpy.notice("Traversing " + str(p))
      if c.__class__.__name__ != 'list':
        raise plpy.Error("  The value here is not a list.")
      else:
        c = c[p]

    if c is None:
      break    

return json.dumps(a)
$$ language plpython2u ;

Exemple d'utilisation:

create table jsonb_table (jsonb_column jsonb);
insert into jsonb_table values
('{"cars":["Jaguar", {"type":"Unknown","partsList":[12, 34, 56]}, "Atom"]}');

select jsonb_column->'cars'->1->'partsList'->2, jsonb_column from jsonb_table;

update jsonb_table
set jsonb_column = json_set(jsonb_column, '["cars",1,"partsList",2]', '99');

select jsonb_column->'cars'->1->'partsList'->2, jsonb_column from jsonb_table;

Notez que pour un employeur précédent, j'ai écrit un ensemble de fonctions C pour manipuler les données JSON sous forme de texte (et non comme un jsonou jsonbtype) pour PostgreSQL 7, 8 et 9. Par exemple, extraire des données avec json_path('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']'), définir des données avec json_path_set('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']', '99.87')et ainsi de suite. Cela a pris environ 3 jours de travail, donc si vous en avez besoin pour fonctionner sur des systèmes hérités et avoir du temps à perdre, cela en vaut la peine. J'imagine que la version C est beaucoup plus rapide que la version python.

Magnus
la source
3

Cela a fonctionné pour moi lorsque j'ai essayé de mettre à jour un champ de type chaîne.

UPDATE table_name 
SET body = jsonb_set(body, '{some_key}', to_json('value'::TEXT)::jsonb);

J'espère que ça aide quelqu'un d'autre!

En supposant que la table nom_table possède une colonne jsonb nommée body et que vous souhaitez modifier body.some_key = 'value'

Antonio
la source
malheureusement, cela reformate JSON de la même manière que les manipulations via les fonctions spécifiques à JSON
Lu55
2

Même si ce qui suit ne satisfera pas cette demande (la fonction json_object_agg n'est pas disponible dans PostgreSQL 9.3), ce qui suit peut être utile pour tous ceux qui recherchent un || pour PostgreSQL 9.4, tel qu'implémenté dans le prochain PostgreSQL 9.5:

CREATE OR REPLACE FUNCTION jsonb_merge(left JSONB, right JSONB)
RETURNS JSONB
AS $$
SELECT
  CASE WHEN jsonb_typeof($1) = 'object' AND jsonb_typeof($2) = 'object' THEN
       (SELECT json_object_agg(COALESCE(o.key, n.key), CASE WHEN n.key IS NOT NULL THEN n.value ELSE o.value END)::jsonb
        FROM jsonb_each($1) o
        FULL JOIN jsonb_each($2) n ON (n.key = o.key))
   ELSE 
     (CASE WHEN jsonb_typeof($1) = 'array' THEN LEFT($1::text, -1) ELSE '['||$1::text END ||', '||
      CASE WHEN jsonb_typeof($2) = 'array' THEN RIGHT($2::text, -1) ELSE $2::text||']' END)::jsonb
   END     
$$ LANGUAGE sql IMMUTABLE STRICT;
GRANT EXECUTE ON FUNCTION jsonb_merge(jsonb, jsonb) TO public;
CREATE OPERATOR || ( LEFTARG = jsonb, RIGHTARG = jsonb, PROCEDURE = jsonb_merge );
Ziggy Crueltyfree Zeitgeister
la source
2

J'ai écrit une petite fonction pour moi qui fonctionne récursivement dans Postgres 9.4. Voici la fonction (j'espère que cela fonctionne bien pour vous):

CREATE OR REPLACE FUNCTION jsonb_update(val1 JSONB,val2 JSONB)
RETURNS JSONB AS $$
DECLARE
    result JSONB;
    v RECORD;
BEGIN
    IF jsonb_typeof(val2) = 'null'
    THEN 
        RETURN val1;
    END IF;

    result = val1;

    FOR v IN SELECT key, value FROM jsonb_each(val2) LOOP

        IF jsonb_typeof(val2->v.key) = 'object'
            THEN
                result = result || jsonb_build_object(v.key, jsonb_update(val1->v.key, val2->v.key));
            ELSE
                result = result || jsonb_build_object(v.key, v.value);
        END IF;
    END LOOP;

    RETURN result;
END;
$$ LANGUAGE plpgsql;

Voici un exemple d'utilisation:

select jsonb_update('{"a":{"b":{"c":{"d":5,"dd":6},"cc":1}},"aaa":5}'::jsonb, '{"a":{"b":{"c":{"d":15}}},"aa":9}'::jsonb);
                            jsonb_update                             
---------------------------------------------------------------------
 {"a": {"b": {"c": {"d": 15, "dd": 6}, "cc": 1}}, "aa": 9, "aaa": 5}
(1 row)

Comme vous pouvez le voir, analyser en profondeur et mettre à jour / ajouter des valeurs si nécessaire.

J. Raczkiewicz
la source
1

Malheureusement, je n'ai rien trouvé dans la documentation, mais vous pouvez utiliser une solution de contournement, par exemple, vous pouvez écrire une fonction étendue.

Par exemple, en Python:

CREATE or REPLACE FUNCTION json_update(data json, key text, value json)
returns json
as $$
from json import loads, dumps
if key is None: return data
js = loads(data)
js[key] = value
return dumps(js)
$$ language plpython3u

puis

update test set data=json_update(data, 'a', to_json(5)) where data->>'b' = '2';
Roman Pekar
la source
Dommage qu'Amazon RDS ne prenne pas en charge plpython3u!
dbau
2
Le valuenécessitera également un loadslors de la définition de valeurs non numériques comme des chaînes ( js[key] = loads(value)) - Sinon:select json_update('{"a":"a"}', 'a', to_json('b')); -> {"a": "\"b\""}
hooblei
Cette réponse pourrait également être modifiée pour inclure la suppression d'une clé lorsque la valeur est définie sur None: `si la valeur est None: del data [clé]
Joshua Burns
1

L'extrait de code plpython suivant peut être utile.

CREATE EXTENSION IF NOT EXISTS plpythonu;
CREATE LANGUAGE plpythonu;

CREATE OR REPLACE FUNCTION json_update(data json, key text, value text)
 RETURNS json
 AS $$
    import json
    json_data = json.loads(data)
    json_data[key] = value
    return json.dumps(json_data, indent=4)
 $$ LANGUAGE plpythonu;

-- Check how JSON looks before updating

SELECT json_update(content::json, 'CFRDiagnosis.mod_nbs', '1')
FROM sc_server_centre_document WHERE record_id = 35 AND template = 'CFRDiagnosis';

-- Once satisfied update JSON inplace

UPDATE sc_server_centre_document SET content = json_update(content::json, 'CFRDiagnosis.mod_nbs', '1')
WHERE record_id = 35 AND template = 'CFRDiagnosis';
Sandeep
la source
1

J'ai trouvé que les réponses précédentes conviennent aux utilisateurs expérimentés de PostgreSQL, d'où ma réponse:

Supposons que vous ayez une table-colonne de type JSONB avec la valeur suivante:

{
    "key0": {
        "key01": "2018-05-06T12:36:11.916761+00:00",
        "key02": "DEFAULT_WEB_CONFIGURATION",

    "key1": {
        "key11": "Data System",
        "key12": "<p>Health,<p>my address<p>USA",
        "key13": "*Please refer to main screen labeling"
    }
}

supposons que nous voulons définir une nouvelle valeur dans la ligne:

"key13": "*Please refer to main screen labeling"

et placez plutôt la valeur:

"key13": "See main screen labeling"

nous utilisons la fonction json_set () pour attribuer une nouvelle valeur à key13

les paramètres de jsonb_set ()

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

dans " cible " - je placerai le nom de la colonne jsonb (c'est la colonne de la table qui est en cours de modification)

" chemin " - est le "chemin des clés json" menant à (et incluant) la clé que nous allons écraser

" new_value " - c'est la nouvelle valeur que nous assignons

dans notre cas, nous voulons mettre à jour la valeur de key13 qui réside sous key1 (key1 -> key13):

d'où la syntaxe du chemin est: '{key1, key13}' (Le chemin était la partie la plus délicate à craquer - parce que les tutoriels sont terribles)

jsonb_set(jsonb_column,'{key1,key13}','"See main screen labeling"')
Dror
la source
0

Vous pouvez également incrémenter des clés de manière atomique jsonbcomme ceci:

UPDATE users SET counters = counters || CONCAT('{"bar":', COALESCE(counters->>'bar','0')::int + 1, '}')::jsonb WHERE id = 1;

SELECT * FROM users;

 id |    counters
----+------------
  1 | {"bar": 1}

Clé non définie -> suppose une valeur de départ de 0.

Pour une explication plus détaillée, voir ma réponse ici: https://stackoverflow.com/a/39076637

joonas.fi
la source
0

Pour ceux qui utilisent mybatis, voici un exemple de déclaration de mise à jour:

<update id="saveAnswer">
    update quiz_execution set answer_data = jsonb_set(answer_data, concat('{', #{qid}, '}')::text[], #{value}::jsonb), updated_at = #{updatedAt}
    where id = #{id}
</update>


Paramètres:

  • qid, la clé du champ.
  • value, est une chaîne json valide, pour la valeur du champ,
    par exemple convertie de l'objet en chaîne json via jackson,
Eric Wang
la source
0

Ainsi, par exemple, ma chaîne ressemble à ceci: {"a1": {"a11": "x", "a22": "y", "a33": "z"}}

Je mets à jour jsons en utilisant la table temporaire, ce qui est assez bon pour un assez petit nombre de données (<1.000.000). J'ai trouvé un chemin différent, mais je suis parti en vacances et je l'ai oublié ...

Alors. la requête sera quelque chose comme ceci:

with temp_table as (
select 
a.id,
a->'a1'->>'a11' as 'a11',
a->'a1'->>'a22' as 'a22',
a->'a1'->>'a33' as 'a33',
u1.a11updated
from foo a
join table_with_updates u1 on u1.id = a.id)
    update foo a
    set a = ('{"a1": {"a11": "'|| t.a11updated ||'",
        "a22":"'|| t.a22 ||'",
        "a33":"'|| t.a33 ||'"}}')::jsonb
    from temp_table t
    where t.id = a.id;

Cela a plus à voir avec la chaîne qu'avec json, mais ça marche. Fondamentalement, il extrait toutes les données dans la table temporaire, crée une chaîne tout en branchant les trous de concaténation avec les données que vous avez sauvegardées et les convertit en jsonb.

Json_set est peut-être plus efficace, mais j'y parviens toujours. La première fois que j'ai essayé de l'utiliser, j'ai complètement foiré la chaîne ...

Vlad S
la source
1
salut et bienvenue sur StackOverflow! Notez qu'il existe déjà une réponse acceptée à cette question.
hongsy
-2

Si vous effectuez cette requête avec un client de langage de programmation, par exemple à partir de python pycopg2, ou Node Postgres, assurez-vous d'analyser d'abord les nouvelles données vers JSON.

Il pourrait facilement ressembler à un dictionnaire python est identique à un objet JSON mais il ne fait pas d'abord json.dumps sur le dictionnaire.

Un simple extrait de python:

def change_destination(self,parcel_id,destlatlng): query="UPDATE parcels SET destlatlng = '{}' WHERE parcel_id ={};".format(json.dumps(destlatlng), parcel_id) self.cursor.execute(query2) self.connection.commit()

Cryce vraiment
la source