Wie ändere ich Felder im neuen PostgreSQL JSON-Datentyp?


235

Mit postgresql 9.3 kann ich bestimmte Felder eines JSON-Datentyps AUSWÄHLEN, aber wie können Sie sie mit UPDATE ändern? Ich kann keine Beispiele dafür in der postgresql-Dokumentation oder irgendwo online finden. Ich habe das Offensichtliche versucht:

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...

Antworten:


330

Update : Mit PostgreSQL 9.5 gibt es einige jsonbManipulationsfunktionen in PostgreSQL selbst (aber keine für json; Casts sind erforderlich, um jsonWerte zu manipulieren ).

Zusammenführen von 2 (oder mehr) JSON-Objekten (oder Verketten von Arrays):

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

Das Einstellen eines einfachen Schlüssels kann also folgendermaßen erfolgen:

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

Wo <key>sollte Zeichenfolge sein, und <value>kann jeder Typ sein, der to_jsonb()akzeptiert.

Zum Festlegen eines Werts tief in einer JSON-Hierarchie kann die folgende jsonb_set()Funktion verwendet werden:

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

Vollständige Parameterliste von jsonb_set():

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

pathkann auch JSON-Array-Indizes enthalten & negative Ganzzahlen, die dort erscheinen, zählen ab dem Ende von JSON-Arrays. Ein nicht vorhandener, aber positiver JSON-Array-Index hängt das Element jedoch an das Ende des Arrays an:

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

Zum Einfügen in ein JSON-Array (unter Beibehaltung aller ursprünglichen Werte) kann die jsonb_insert()Funktion verwendet werden ( in 9.6+; nur diese Funktion in diesem Abschnitt ):

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

Vollständige Parameterliste von jsonb_insert():

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

Wiederum zählen negative Ganzzahlen, die pathab dem Ende der JSON-Arrays angezeigt werden.

Also, z. Das Anhängen an ein Ende eines JSON-Arrays kann erfolgen mit:

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

Diese Funktion funktioniert jedoch etwas anders (als jsonb_set()), wenn das pathIn targetder Schlüssel eines JSON-Objekts ist. In diesem Fall wird nur dann ein neues Schlüssel-Wert-Paar für das JSON-Objekt hinzugefügt, wenn der Schlüssel nicht verwendet wird. Wenn es verwendet wird, wird ein Fehler ausgegeben:

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

Das Löschen eines Schlüssels (oder eines Index) aus einem JSON-Objekt (oder aus einem Array) kann mit dem -Operator erfolgen:

SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
       jsonb '["a",1,"b",2]' - 1    -- will yield jsonb '["a","b",2]'

Das Löschen aus einer JSON-Hierarchie heraus kann mit dem #-Operator erfolgen:

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

Für 9.4 können Sie eine geänderte Version der ursprünglichen Antwort (unten) verwenden. Anstatt jedoch eine JSON-Zeichenfolge zu aggregieren, können Sie direkt mit zu einem JSON-Objekt aggregieren json_object_agg().

Ursprüngliche Antwort : Es ist möglich (ohne plpython oder plv8) auch in reinem SQL (benötigt aber 9.3+, funktioniert nicht mit 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

Bearbeiten :

Eine Version, die mehrere Schlüssel und Werte festlegt:

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

Bearbeiten 2 : Wie @ErwinBrandstetter feststellte, funktionieren diese Funktionen wie ein sogenanntes UPSERT(aktualisiert ein Feld, falls vorhanden, fügt ein, wenn es nicht vorhanden ist). Hier ist eine Variante, die nur 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$;

Bearbeiten 3 : Hier ist eine rekursive Variante, die UPSERTeinen Blattwert setzen ( ) kann (und die erste Funktion aus dieser Antwort verwendet), die sich an einem Schlüsselpfad befindet (wobei Schlüssel nur auf innere Objekte verweisen können, innere Arrays nicht unterstützt werden):

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

Update : Funktionen sind jetzt komprimiert.


5
Ich habe Ihre plpgsql-Funktion ausprobiert, bin mir aber nicht sicher, wie ich sie verwenden soll. Beim Versuch der select json_object_set_key((select data from test where data->>'b' = '2'), 'b', 'two'); Fehlermeldung wird ein Fehler ERROR: could not determine polymorphic type because input has type "unknown"
angezeigt

1
Dies entspricht einem UPSERT, nicht einem UPDATE. Wenn der Schlüssel noch nicht im Feld json vorhanden ist, wird er hinzugefügt. Schauen Sie sich diese verwandte Frage für eine aktuelle an UPDATE: stackoverflow.com/questions/7711432/… (Dies ist für einen zusammengesetzten Typ, aber das Prinzip ist für json ähnlich.)
Erwin Brandstetter

1
@ErwinBrandstetter das ist wahr, aber in json ist ein UPSERT normalerweise allgemeiner als eine UPDATE-ähnliche Modifikation (siehe f.ex. sqlfiddle.com/#!15/d41d8/2897 ) - Ich habe die ursprüngliche Frage so interpretiert, wie Sie modifizieren sie (JSON-Spalten) mit einer UPDATE-Anweisung? - Außerdem könnte eine einzelne Bedingung dies in UPDATE umwandeln.
pozs

1
Sehr nützlich und jetzt vollständig.
Erwin Brandstetter

1
@maxhud, das vom Client (oder der von Ihnen verwendeten Clientbibliothek) abhängt. Wenn Sie können, verwenden Sie explizite Typen (PostgreSQL kann Typen in parametrisierten Abfragen erraten, aber das funktioniert normalerweise nicht gut mit polymorphen Funktionen). Aber zumindest können Sie explizite Casts verwenden, wie z $2::text.
pozs

98

Verwenden Sie mit 9.5 jsonb_set-

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

Dabei ist body ein jsonb-Spaltentyp.


Hallo, warum kann ich das nicht so verwenden upper: update objects set body=jsonb_set(body, '{name}', upper('"Mary"'), true) where id=1;es erkennt nicht, oder wie kann ich das gleiche Verhalten erreichen? thx
Rafael Capucho

1
Wenn der Wert, den ich festlegen möchte, ein Teilstring aus einer anderen Spalte und nicht "Mary" ist, wie würde ich das tun?
Andrew

58

Mit Postgresql 9.5 können Sie Folgendes tun:

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

ODER

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

Jemand fragte, wie viele Felder im jsonb-Wert gleichzeitig aktualisiert werden könnten. Angenommen, wir erstellen eine Tabelle:

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

Dann fügen wir eine experimentelle Zeile ein:

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

Dann aktualisieren wir die Zeile:

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

Welches macht das Folgende:

  1. Aktualisiert das a-Feld
  2. Entfernt das Feld b
  3. Fügen Sie das Feld d hinzu

Daten auswählen:

SELECT jsonb_pretty(object) FROM testjsonb;

Wird darin enden, dass:

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

Verwenden Sie zum Aktualisieren des Felds nicht den concat-Operator ||. Verwenden Sie stattdessen jsonb_set. Welches ist nicht einfach:

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

Verwenden Sie den concat-Operator für {c, c1} zum Beispiel:

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

Entfernt {c, c2} und {c, c3}.

Weitere Informationen finden Sie in der Dokumentation zu den Funktionen von postgresql json . Man könnte sich für den #-Bediener, die jsonb_setFunktion und auch die jsonb_insertFunktion interessieren .


und wenn ich zwei Felder aktualisieren muss, wie lautet dann die Syntax?
Sunil Garg

Wenn ich eine JSON-Spalte mit Feldnamen habe, wie füge ich dieser Spalte ein
Nachnamenfeld hinzu

Sollte klar sein:UPDATE users SET profile = profile || '{"lastname":"Washington"}' WHERE profile->>'name' = 'George Washington';
Fandi Susanto

9

Um auf den Antworten von @ pozs aufzubauen, finden Sie hier einige weitere PostgreSQL-Funktionen, die für einige nützlich sein können. (Erfordert PostgreSQL 9.3+)

Nach Schlüssel löschen : Löscht einen Wert aus der JSON-Struktur nach Schlüssel.

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

Rekursives Löschen nach Schlüssel: Löscht einen Wert aus der JSON-Struktur nach Schlüsselpfad. (erfordert die json_object_set_keyFunktion von @ 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$;

Anwendungsbeispiele:

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"}}

Sehr hilfreich! Danke dir.
1111161171159459134

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

Dies scheint unter PostgreSQL 9.5 zu funktionieren


Funktioniert für mich, soweit ich verstanden habe, das Feld "a" aus den Daten zu entfernen und dann das Feld "a" mit dem neuen Wert anzuhängen. In meinem Fall basierte der Wert von "a" auf einer Spalte. UPDATE-Test SET data = data :: jsonb - 'a' || ('{"a": "' || myColumn || '"}') :: jsonb;
sebge2

7

Wenn Ihr Feldtyp json ist, funktioniert Folgendes für Sie.

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

Operator '-' Schlüssel / Wert-Paar oder String-Element aus dem linken Operanden löschen. Schlüssel / Wert-Paare werden basierend auf ihrem Schlüsselwert abgeglichen.

Operator '||' Verketten Sie zwei Jsonb-Werte zu einem neuen Jsonb-Wert.

Da es sich um jsonb-Operatoren handelt, müssen Sie nur Folgendes in :: jsonb eingeben

Weitere Informationen: JSON-Funktionen und Operatoren

Sie können meine Notiz hier lesen


Einfache und bessere Möglichkeit, JSON-Felder zu aktualisieren, wenn Sie sich keine Sorgen über die Neuordnung der Eigenschaftsreihenfolge machen.
Karthik Sivaraj

4

Mit PostgreSQL 9.4 haben wir die folgende Python-Funktion implementiert. Es kann auch mit PostgreSQL 9.3 funktionieren.

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 ;

Anwendungsbeispiel:

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;

Beachten Sie, dass ich für einen früheren Arbeitgeber eine Reihe von C-Funktionen zum Bearbeiten von JSON-Daten als Text (nicht als jsonoder jsonbTyp) für PostgreSQL 7, 8 und 9 geschrieben habe. Beispiel: Extrahieren von Daten mit json_path('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']'), Festlegen von Daten mit json_path_set('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']', '99.87')usw. Die Arbeit dauerte ungefähr 3 Tage. Wenn Sie es also benötigen, um auf Legacy-Systemen ausgeführt zu werden und Zeit zu haben, ist es möglicherweise die Mühe wert. Ich stelle mir vor, die C-Version ist viel schneller als die Python-Version.


2

Obwohl das Folgende diese Anforderung nicht erfüllt (die Funktion json_object_agg ist in PostgreSQL 9.3 nicht verfügbar), kann das Folgende für jeden nützlich sein, der nach einem || sucht Operator für PostgreSQL 9.4, wie im kommenden PostgreSQL 9.5 implementiert:

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

2

Ich habe eine kleine Funktion für mich geschrieben, die in Postgres 9.4 rekursiv funktioniert. Hier ist die Funktion (ich hoffe, es funktioniert gut für Sie):

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;

Hier ist ein Beispiel für die Verwendung:

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)

Wie Sie sehen können, analysieren Sie tief im Inneren und aktualisieren / fügen Sie Werte hinzu, wo dies erforderlich ist.


2

Dies funktionierte bei mir, als ich versuchte, ein Zeichenfolgentypfeld zu aktualisieren.

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

Hoffe es hilft jemand anderem!

Angenommen, die Tabelle table_name hat eine jsonb-Spalte mit dem Namen body und Sie möchten body.some_key = 'value' ändern.


Leider formatiert dies JSON genauso neu wie Manipulationen durch die JSON-spezifischen Funktionen
Lu55

1

Leider habe ich in der Dokumentation nichts gefunden, aber Sie können eine Problemumgehung verwenden, z. B. eine erweiterte Funktion schreiben.

Zum Beispiel in 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

und dann

update test set data=json_update(data, 'a', to_json(5)) where data->>'b' = '2';

Schade, dass Amazon RDS plpython3u nicht unterstützt!
Bau

2
Das valueerfordert auch ein, loadswenn nicht numerische Werte wie Strings ( js[key] = loads(value)) gesetzt werden - Andernfalls:select json_update('{"a":"a"}', 'a', to_json('b')); -> {"a": "\"b\""}
Hooblei

Diese Antwort könnte auch geändert werden, um das Löschen eines Schlüssels einzuschließen, wenn der Wert auf Keine gesetzt ist: `Wenn der Wert Keine ist: Daten löschen [Schlüssel]
Joshua Burns

1

Das folgende plpython-Snippet kann nützlich sein.

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

1

Ich habe festgestellt, dass frühere Antworten für erfahrene PostgreSQL-Benutzer geeignet sind, daher meine Antwort:

Angenommen, Sie haben eine Tabellenspalte vom Typ JSONB mit dem folgenden Wert:

{
    "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"
    }
}

Nehmen wir an, wir möchten einen neuen Wert in der Zeile festlegen:

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

und platzieren Sie stattdessen den Wert:

"key13": "See main screen labeling"

Wir verwenden die Funktion json_set (), um dem Schlüssel13 einen neuen Wert zuzuweisen

die Parameter zu jsonb_set ()

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

in " target " - Ich werde den jsonb-Spaltennamen platzieren (dies ist die Tabellenspalte, die geändert wird).

" path " - ist der "json keys path", der zu dem Schlüssel führt (und ihn einschließt), den wir überschreiben werden

" new_value " - Dies ist der neue Wert, den wir zuweisen

In unserem Fall möchten wir den Wert von key13 aktualisieren, der sich unter key1 befindet (key1 -> key13):

daher auch die Pfadsyntax ist: ‚{key1, key13}‘ (Der Weg war der schwierige Teil zu knacken - weil die Tutorials sind schrecklich)

jsonb_set(jsonb_column,'{key1,key13}','"See main screen labeling"')

0

Sie können Schlüssel auch atomar jsonbwie folgt inkrementieren :

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}

Undefinierter Schlüssel -> nimmt den Startwert 0 an.

Eine ausführlichere Erklärung finden Sie in meiner Antwort hier: https://stackoverflow.com/a/39076637


0

Für diejenigen, die verwenden mybatis, ist hier ein Beispiel für eine Update-Anweisung:

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


Parameter:

  • qid, der Schlüssel für Feld.
  • valueist eine gültige JSON-Zeichenfolge für den Feldwert,
    z. B. konvertiert von Objekt zu JSON-Zeichenfolge über jackson,

0

So sieht meine Zeichenfolge beispielsweise folgendermaßen aus: {"a1": {"a11": "x", "a22": "y", "a33": "z"}}

Ich aktualisiere jsons mithilfe einer temporären Tabelle, die für eine relativ kleine Datenmenge (<1.000.000) ausreicht. Ich habe einen anderen Weg gefunden, bin dann aber in den Urlaub gefahren und habe ihn vergessen ...

So. Die Abfrage wird ungefähr so ​​aussehen:

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;

Es hat mehr mit String als mit Json zu tun, aber es funktioniert. Grundsätzlich werden alle Daten in eine temporäre Tabelle gezogen, eine Zeichenfolge erstellt, während Concat-Löcher mit den von Ihnen gesicherten Daten geschlossen werden, und in jsonb konvertiert.

Json_set ist vielleicht effizienter, aber ich verstehe es immer noch. Als ich das erste Mal versuchte, es zu benutzen, habe ich die Saite komplett durcheinander gebracht ...


1
Hallo und willkommen bei StackOverflow! Beachten Sie, dass diese Frage bereits akzeptiert wurde.
Hongsy

-2

Wenn Sie diese Abfrage mit einem Programmiersprachen-Client durchführen, z. B. von python pycopg2oder Node Postgres, Stellen Sie sicher, dass Sie die neuen Daten zuerst in JSON analysieren.

Es könnte leicht so aussehen, als wäre ein Python-Wörterbuch dasselbe wie ein JSON-Objekt, aber es führt nicht zuerst json.dumps im Wörterbuch aus.

Ein einfaches Python-Snippet:

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()

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.