it-swarm.com.de

Ausführen von Aktualisierungsvorgängen für Spalten des Typs JSONB in ​​Postgres 9.4

In der Dokumentation zum Postgres 9.4-Datentyp JSONB ist mir nicht sofort klar, wie Aktualisierungen an JSONB-Spalten vorgenommen werden.

Dokumentation für JSONB-Typen und -Funktionen:

http://www.postgresql.org/docs/9.4/static/functions-json.htmlhttp://www.postgresql.org/docs/9.4/static/datatype- json.html

Als Beispiel habe ich diese grundlegende Tabellenstruktur:

CREATE TABLE test(id serial, data jsonb);

Einfügen ist einfach, wie in:

INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}');

Wie aktualisiere ich nun die Spalte "Daten"? Dies ist eine ungültige Syntax:

UPDATE test SET data->'name' = 'my-other-name' WHERE id = 1;

Ist das irgendwo offensichtlich dokumentiert, was ich verpasst habe? Vielen Dank.

102
jvous

Idealerweise verwenden Sie keine JSON-Dokumente für strukturierte, reguläre Daten, die Sie in einer relationalen Datenbank bearbeiten möchten. Verwenden Sie stattdessen ein normalisiertes relationales Design .

JSON dient in erster Linie zum Speichern ganzer Dokumente, die nicht im RDBMS bearbeitet werden müssen. Verbunden:

Beim Aktualisieren einer Zeile in Postgres wird immer eine neue Version der Zeile whole geschrieben. Das ist das Grundprinzip von Postgres 'MVCC-Modell . Aus Sicht der Leistung spielt es kaum eine Rolle, ob Sie ein einzelnes Datenelement in einem JSON-Objekt oder das gesamte Datenelement ändern: Es muss eine neue Version der Zeile geschrieben werden.

Also der Hinweis im Handbuch :

JSON-Daten unterliegen beim Speichern in einer Tabelle denselben Überlegungen zur Parallelitätskontrolle wie alle anderen Datentypen. Obwohl das Speichern großer Dokumente sinnvoll ist, sollten Sie berücksichtigen, dass jedes Update eine Sperre auf Zeilenebene für die gesamte Zeile erhält. Beschränken Sie JSON-Dokumente auf eine überschaubare Größe, um Sperrenkonflikte bei der Aktualisierung von Transaktionen zu vermeiden. Idealerweise sollten JSON-Dokumente jeweils ein atomares Datum darstellen, das die Geschäftsregeln vorschreiben, und das nicht weiter in kleinere Daten unterteilt werden kann, die unabhängig voneinander geändert werden können.

Der Kern: Um irgendetwas innerhalb eines JSON-Objekts zu ändern, müssen Sie der Spalte ein geändertes Objekt zuweisen. Postgres bietet begrenzte Möglichkeiten zum Erstellen und Bearbeiten von json Daten zusätzlich zu den Speichermöglichkeiten. Das Arsenal an Tools ist seit Version 9.2 mit jeder neuen Version erheblich gewachsen. Das Prinzip bleibt jedoch: Sie müssen der Spalte always ein vollständig geändertes Objekt zuweisen, und Postgres schreibt bei jeder Aktualisierung immer eine neue Zeilenversion.

Einige Techniken zum Arbeiten mit den Werkzeugen von Postgres 9.3 oder höher:

Diese Antwort hat ungefähr so ​​viele Ablehnungen hervorgerufen wie alle meine anderen Antworten auf SO zusammen. Die Idee scheint den Leuten nicht zu gefallen: Ein normalisiertes Design ist für nicht dynamische Daten überlegen. In diesem ausgezeichneten Blog-Beitrag von Craig Ringer wird Folgendes näher erläutert:

35

Wenn Sie ein Upgrade auf Postgresql 9.5 durchführen können, wird jsonb_set Befehl ist verfügbar, wie andere erwähnt haben.

In jeder der folgenden SQL-Anweisungen habe ich die where -Klausel aus Gründen der Kürze weggelassen. Natürlich möchten Sie das wieder hinzufügen.

Name aktualisieren:

UPDATE test SET data = jsonb_set(data, '{name}', '"my-other-name"');

Ersetzen Sie die Tags (im Gegensatz zum Hinzufügen oder Entfernen von Tags):

UPDATE test SET data = jsonb_set(data, '{tags}', '["tag3", "tag4"]');

Ersetzen des zweiten Tags (0-indiziert):

UPDATE test SET data = jsonb_set(data, '{tags,1}', '"tag5"');

Tag anhängen (dies funktioniert, solange weniger als 999 Tags vorhanden sind. Das Ändern des Arguments 999 auf 1000 oder höher erzeugt einen Fehler. Dies scheint in Postgres 9.5.3 nicht mehr der Fall zu sein; ein viel größerer Index kann verwendet werden):

UPDATE test SET data = jsonb_set(data, '{tags,999999999}', '"tag6"', true);

Entfernen Sie das letzte Tag:

UPDATE test SET data = data #- '{tags,-1}'

Komplexes Update (Löschen Sie das letzte Tag, fügen Sie ein neues Tag ein und ändern Sie den Namen):

UPDATE test SET data = jsonb_set(
    jsonb_set(data #- '{tags,-1}', '{tags,999999999}', '"tag3"', true), 
    '{name}', '"my-other-name"');

Beachten Sie, dass Sie in jedem dieser Beispiele kein einzelnes Feld der JSON-Daten aktualisieren. Stattdessen erstellen Sie eine temporäre, geänderte Version der Daten und weisen diese geänderte Version der Spalte zu. In der Praxis sollte das Ergebnis dasselbe sein, aber wenn dies berücksichtigt wird, sollten komplexe Aktualisierungen, wie das letzte Beispiel, verständlicher werden.

In dem komplexen Beispiel gibt es drei Transformationen und drei temporäre Versionen: Zuerst wird das letzte Tag entfernt. Anschließend wird diese Version durch Hinzufügen eines neuen Tags transformiert. Als nächstes wird die zweite Version transformiert, indem das Feld name geändert wird. Der Wert in der Spalte data wird durch die endgültige Version ersetzt.

223
Jimothy

Dies kommt in 9.5 in Form von jsonb_set von Andrew Dunstan basierend auf einer vorhandenen Erweiterung jsonbx , die mit 9.4 funktioniert

23

Für diejenigen, die auf dieses Problem stoßen und eine sehr schnelle Fehlerbehebung wünschen (und auf 9.4.5 oder früher stecken bleiben), ist hier, was ich getan habe:

Erstellung der Prüftabelle

CREATE TABLE test(id serial, data jsonb);
INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}');

pdate-Anweisung, um den Namen der jsonb-Eigenschaft zu ändern

UPDATE test 
SET data = replace(data::TEXT,'"name":','"my-other-name":')::jsonb 
WHERE id = 1;

Letztendlich ist die akzeptierte Antwort insofern richtig, als Sie ein einzelnes Teil eines JSONB-Objekts (in 9.4.5 oder früher) nicht ändern können. Sie können das jsonb-Objekt jedoch in eine Zeichenfolge umwandeln (:: TEXT) und dann die Zeichenfolge bearbeiten und zurück in das jsonb-Objekt umwandeln (:: jsonb).

Es gibt zwei wichtige Vorbehalte

  1. dadurch werden alle Eigenschaften mit dem Namen "name" im json ersetzt (falls Sie mehrere Eigenschaften mit demselben Namen haben).
  2. dies ist nicht so effizient wie jsonb_set, wenn Sie 9.5 verwenden

Nachdem dies gesagt wurde, stieß ich auf eine Situation, in der ich das Schema für den Inhalt in den jsonb-Objekten aktualisieren musste, und dies war der einfachste Weg, um genau das zu erreichen, wonach das ursprüngliche Poster fragte.

14
Chad Capra

Diese Frage wurde im Kontext von postgres 9.4 gestellt. Neue Leser sollten jedoch wissen, dass in postgres 9.5 Subdokument-Erstellungs-/Aktualisierungs-/Löschvorgänge für JSONB-Felder von der Datenbank nativ unterstützt werden, ohne dass eine Erweiterung erforderlich ist funktionen.

Siehe: Ändern von Operatoren und Funktionen durch JSONB

9
bguiz

Ich habe eine kleine Funktion für mich geschrieben, die in Postgres 9.4 rekursiv funktioniert. Ich hatte das gleiche Problem (gut, sie haben einige dieser Kopfschmerzen in Postgres 9.5 gelöst). Jedenfalls ist hier 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 Anwendungsbeispiel:

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 tiefgehend und aktualisieren/fügen Sie Werte hinzu, wo dies erforderlich ist.

5
J. Raczkiewicz

Vielleicht: UPDATE test SET data = '"mein-anderer-name"' :: json WHERE id = 1;

Es hat in meinem Fall funktioniert, in dem Daten vom Typ JSON sind

3

aktualisieren Sie das Attribut 'name':

UPDATE test SET data=data||'{"name":"my-other-name"}' WHERE id = 1;

und wenn Sie zum Beispiel die Attribute 'name' und 'tags' entfernen möchten:

UPDATE test SET data=data-'{"name","tags"}'::text[] WHERE id = 1;
3
Arthur

Matheus de Oliveira hat praktische Funktionen für JSON CRUD-Operationen in postgresql erstellt. Sie können mit der Direktive\i importiert werden. Beachten Sie die Jsonb-Verzweigung der Funktionen, wenn Jsonb Ihr Datentyp ist.

9.3 json https://Gist.github.com/matheusoliveira/9488951

9.4 jsonb https://Gist.github.com/inindev/2219dff96851928c2282

2
John Clark