MySQL 5.7+,嵌套路径中的JSON_SET值

Baz*_*xer 7 php mysql json mysql-json

对于最近的开发项目,我们使用MySQL 5.7,因此我们可以利用最新的JSON功能......

我正在构建一个UPDATE查询,其中应该将嵌套的json对象插入/添加到JSON类型的attributes-column中,请参阅下面的查询.

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

当我执行此查询时,attributes-field包含数据

{"test1": "Test 1"} 
Run Code Online (Sandbox Code Playgroud)

而不是通缉

{"test1", "Test 1", "test2": {"test3", "Test 3"}}
Run Code Online (Sandbox Code Playgroud)

还尝试使用JSON_MERGE,但是当我多次执行它时,它会创建一个类似的JSON对象

{"test1": ["Test 1", "Test 1", "Test 1"... etc.], "test2": {"test3": ["Test 3", "Test 3", "Test 3"... etc.]}}
Run Code Online (Sandbox Code Playgroud)

那么,当节点不存在时,JSON_SET不起作用?JSON_MERGE合并到无穷大?

JSON对象中使用的键可以由用户定义,因此无法为所有可能的键创建空的JSON对象.我们是否真的需要在每个UPDATE查询之前执行JSON_CONTAINS/JSON_CONTAINS_PATH查询,以确定是否需要使用JSON_SET或JSON_MERGE/JSON_APPEND?

我们正在寻找一种方法来获得始终有效的查询,所以当"$.test4.test5.test6"给出时,它将扩展当前的JSON对象,添加完整路径......如何做到这一点?

fyr*_*rye 11

假设你想要最终的结果

{"test1": "Test 1", "test2": {"test3": "Test 3"}}
Run Code Online (Sandbox Code Playgroud)

在您的示例中attributes,正在更新的列设置为{"test1": "Test 1"}

查看您的初始UPDATE查询,我们可以看到$.test2.test3不存在.所以它不能被设置为

JSON_SET()在JSON文档中插入或更新数据并返回结果.如果任何参数为NULL或路径(如果给定)未找到对象,则返回NULL.

意思是MySQL可以添加$.test2,但由于$.test2不是对象,MySQL无法添加$.test2.test3.

因此,您需要$.test2通过执行以下操作将其定义为json对象.

mysql> SELECT * FROM testing;
+----+---------------------+
| id | attributes          |
+----+---------------------+
|  1 | {"test1": "Test 1"} |
+----+---------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
mysql> SELECT * FROM testing;
+----+---------------------------------------------------+
| id | attributes                                        |
+----+---------------------------------------------------+
|  1 | {"test1": "Test 1", "test2": {"test3": "Test 3"}} |
+----+---------------------------------------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

因此,您需要明确地告诉MySQL密钥作为JSON对象存在,而不是依赖于MySQL点表示法.

这类似于PHP也定义了不存在的对象属性值.

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

//PHP Warning:  Creating default object from empty value
Run Code Online (Sandbox Code Playgroud)

要摆脱错误,您需要先定义$a->test2为对象.

$a = (object) ['test1' => 'Test 1'];
$a->test2 = (object) ['test3' => 'Test 3'];
Run Code Online (Sandbox Code Playgroud)

或者,您可以在使用点表示法之前测试和创建对象,以设置值.虽然对于较大的数据集,这可能是不合需要的

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
Run Code Online (Sandbox Code Playgroud)
mysql> SELECT * FROM testing;
+----+---------------------------------------------------------------------------+
| id | attributes                                                                |
+----+---------------------------------------------------------------------------+
|  1 | {"test1": "Test 1", "test2": {"test3": "Test 3"}, "test4": {"test5": {}}} |
+----+---------------------------------------------------------------------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

虽然在任何一种情况下如果未提供原始数据,JSON_OBJECT函数调用将清空嵌套对象的属性值.但正如您在上一个JSON_SET查询中所看到的那样,$.test1未在定义中提供attributes,并且它保持不变,因此可以从查询中省略未修改的属性.

  • 这是否不仅使处理JSON文档成为真正的痛苦?我的意思是在结构未知且嵌套很深的情况下。确切地说,何时才转向JSON IMO。 (2认同)

bor*_*ryn 8

JSON_MERGE_PATCH现在,从 MySQL 版本 5.7.22 开始,最简单的方法是像这样使用:

UPDATE `table` SET `attributes` = 
JSON_MERGE_PATCH(`attributes`, '{"test2": {"test3": "Test 3"}, "test4": {"test5": {}}}')
Run Code Online (Sandbox Code Playgroud)

{"test1": "Test 1", "test2": {"test3": "Test 3"}, "test4": {"test5": {}}}这给出了您的示例中的预期结果。