2016-07-11 9 views
2

Für ein aktuelles Entwicklungsprojekt, wir verwenden MySQL 5.7, also können wir Vorteile der neuesten JSON-Funktionen übernehmen ...MySQL 5.7+, JSON_SET Wert in verschachteltem Pfad

Ich baue eine UPDATE- Abfrage, wo ein verschachteltes json-Objekt in die attributes-Spalte vom Typ JSON eingefügt/eingefügt werden soll, siehe Abfrage unten.

UPDATE `table` SET `table`.`name` = 'Test', 
    `table`.`attributes` = JSON_SET(
     `table`.`attributes`, 
     "$.test1", "Test 1", 
     "$.test2.test3", "Test 3" 
    ) 

Wenn ich diese Abfrage ausführen, enthält die Attribute-Feld der Daten

{"test1": "Test 1"} 

statt der

wollte
{"test1", "Test 1", "test2": {"test3", "Test 3"}} 

Auch versucht JSON_MERGE zu verwenden, aber wenn ich ausführen es mehrere Mal erstellt es ein JSON-Objekt wie

{"test1": ["Test 1", "Test 1", "Test 1"... etc.], "test2": {"test3": ["Test 3", "Test 3", "Test 3"... etc.]}} 

Also, JSON_SET funktioniert nicht, wenn Knoten nicht existieren? JSON_MERGE verschmilzt bis unendlich?

Die im JSON-Objekt verwendeten Schlüssel können vom Benutzer definiert werden, daher ist es nicht möglich, ein leeres JSON-Objekt für alle möglichen Schlüssel zu erstellen. Müssen wir wirklich vor jeder UPDATE-Abfrage eine JSON_CONTAINS/JSON_CONTAINS_PATH-Abfrage ausführen, um festzustellen, ob wir JSON_SET oder JSON_MERGE/JSON_APPEND verwenden müssen?

Wir suchen nach einer Möglichkeit, eine Abfrage zu haben, die immer funktioniert. Wenn also "$.test4.test5.test6" angegeben wird, wird das aktuelle JSON-Objekt erweitert und der vollständige Pfad hinzugefügt ... Wie kann das gemacht werden?

Antwort

1

Fyrye, danke für die Grusskarte, ich schätze es sehr! Da die Daten keine feste Struktur haben und für jeden einzelnen Datensatz unterschiedlich sein können, benötigte ich eine Lösung, mit der ich eine Abfrage erzeugen konnte, die automatisch das gesamte JSON-Objekt in einer einzigen Abfrage erzeugte.

Ich mag Ihre Lösung mit der JSON_SET(attributes, "$.test2", IFNULL(attributes->'$.test2',JSON_OBJECT())) Methode. Weil ich meine Suche fortsetzte, fand ich auch selbst eine Lösung mit JSON_MERGE Funktion.

Wenn ich ein Update ausführe, verwende ich JSON_MERGE, um ein leeres JSON-Objekt auf das Feld in der Datenbank für alle Schlüssel mit Unterknoten zusammenzuführen, so dass sie im JSON-Feld in der Datenbank verfügbar sind und danach JSON_SET verwenden, um Werte zu aktualisieren. So ist die komplette Abfrage sieht wie folgt aus:

UPDATE table SET 
    -> attributes = JSON_MERGE(
    -> attributes, '{"test2": {}, "test4": {"test5": {}}}'), 
    -> attributes = JSON_SET(attributes, "$.test2.test3", "Test 3"); 

Nach dieser Abfrage ausgeführt wird, wird das Ergebnis in etwa so aussehen:

mysql> SELECT * FROM testing; 
+----+---------------------------------------------------------------------------+ 
| id | attributes                | 
+----+---------------------------------------------------------------------------+ 
| 1 | {"test1": "Test 1", "test2": {"test3": "Test 3"}, "test4": {"test5": {}}} | 
+----+---------------------------------------------------------------------------+ 
1 row in set (0.00 sec) 

Ich weiß nicht, welche Methode besser ist zu diesem Zeitpunkt, beide arbeiten zur Zeit. Werde in Zukunft einige Geschwindigkeitstests durchführen, um zu überprüfen, wie sie bei 1 Update 10.000 Zeilen Preform werden!

+0

Das ist echte Schmerzen IMO. Ich habe eine Feature-Anforderung http://bugs.mysql.com/bug.php?id=84167 eingereicht. Vielleicht, wenn Sie Minute haben, können Sie es unterstützen, indem Sie auf die Schaltfläche Effekte mich klicken:) Oder kommentieren. –

2

Angenommen, Sie wollen ein Endergebnis von

{"test1": "Test 1", "test2": {"test3": "Test 3"}} 

In Ihrem Beispiel die attributes Spalte, die aktualisiert wird wird auf {"test1": "Test 1"}

Ein Blick auf Ihre anfänglichen UPDATE Abfrage, können wir sehen $.test2.test3 nicht existiert . So kann es nicht als JSON_SET() Inserts or updates data in a JSON document and returns the result. Returns NULL if any argument is NULL or path, if given, does not locate an object.

MySQL gesetzt Bedeutung $.test2 hinzufügen können, aber da $.test2 kein Objekt ist, MySQL kann $.test2.test3 nicht erneut auf.

Sie müssten also $.test2 als JSON-Objekt definieren, indem Sie Folgendes tun.

mysql> SELECT * FROM testing; 
+----+---------------------+ 
| id | attributes   | 
+----+---------------------+ 
| 1 | {"test1": "Test 1"} | 
+----+---------------------+ 
1 row in set (0.00 sec) 
mysql> UPDATE testing 
    -> SET attributes = JSON_SET(
    -> attributes, 
    -> "$.test1", "Test 1", 
    -> "$.test2", JSON_OBJECT("test3", "Test 3") 
    ->); 
Query OK, 1 row affected (0.03 sec) 
Rows matched: 1 Changed: 1 Warnings: 0 
mysql> SELECT * FROM testing; 
+----+---------------------------------------------------+ 
| id | attributes          | 
+----+---------------------------------------------------+ 
| 1 | {"test1": "Test 1", "test2": {"test3": "Test 3"}} | 
+----+---------------------------------------------------+ 
1 row in set (0.00 sec) 

Anstatt also auf der MySQL-Punktnotation zu verlassen, würden Sie ausdrücklich MySQL muss sagen, dass der Schlüssel als JSON-Objekt vorhanden ist.

Dies ähnelt der Definition von nicht vorhandenen Objekteigenschaftswerten durch PHP.

$a = (object) ['test1' => 'Test 1']; 
$a->test2->test3 = 'Test 3'; 

//PHP Warning: Creating default object from empty value 

Um die Fehler loszuwerden, würden Sie zuerst $a->test2 als Objekt definieren müssen.

Alternativ können Sie die Objekte testen und erstellen, bevor Sie die Punktnotation verwenden, um die Werte festzulegen. Bei größeren Datensätzen kann dies jedoch unerwünscht sein.

mysql> UPDATE testing 
    -> SET 
    -> attributes = JSON_SET(attributes, "$.test2", IFNULL(attributes->'$.test2',JSON_OBJECT())), 
    -> attributes = JSON_SET(attributes, "$.test4", IFNULL(attributes->'$.test4', JSON_OBJECT())), 
    -> attributes = JSON_SET(attributes, "$.test4.test5", IFNULL(attributes->'$.test4.test5', JSON_OBJECT())), 
    -> attributes = JSON_SET(
    -> attributes, 
    -> "$.test2.test3", "Test 3" 
    ->); 
Query OK, 1 row affected (0.02 sec) 
Rows matched: 1 Changed: 1 Warnings: 0 
mysql> SELECT * FROM testing; 
+----+---------------------------------------------------------------------------+ 
| id | attributes                | 
+----+---------------------------------------------------------------------------+ 
| 1 | {"test1": "Test 1", "test2": {"test3": "Test 3"}, "test4": {"test5": {}}} | 
+----+---------------------------------------------------------------------------+ 
1 row in set (0.00 sec) 

Obwohl in jedem Fall, wenn die ursprünglichen Daten nicht den JSON_OBJECT Funktionsaufruf zur Verfügung gestellt werden, wird das verschachtelte Objekt des Eigenschaftswert leer aus (s). Aber wie Sie aus der letzten JSON_SET Abfrage sehen können, $.test1 wurde nicht in der Definition von attributes zur Verfügung gestellt, und es blieb intakt, so dass diese Eigenschaften, die nicht geändert werden, aus der Abfrage weggelassen werden können.

+1

Macht dies nicht nur den Umgang mit JSON-Dokumenten zu einem echten Schmerz? Ich meine in dem Fall, wenn die Struktur unbekannt ist und die Verschachtelung tief ist. Was genau, wenn man sich an JSON IMO wendet. –

Verwandte Themen