16

对于最近的一个开发项目,我们使用的是 MySQL 5.7,因此我们可以利用最新的 JSON 函数......

我正在构建一个 UPDATE 查询,其中应将嵌套的 json 对象插入/添加到 JSON 类型的属性列中,请参见下面的查询。

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

当我执行此查询时,属性字段包含数据

{"test1": "Test 1"} 

而不是通缉

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

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

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

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

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

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

4

4 回答 4

20

从 MySQL 版本 5.7.13 开始,假设您希望最终结果为

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

在您的示例中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)
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)

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

这类似于 PHP 如何定义不存在的对象属性值。

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

//PHP Warning:  Creating default object from empty value

要消除错误,您需要首先定义$a->test2为对象。

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

或者,您可以在使用点符号之前测试和创建对象,以设置值。尽管对于较大的数据集,这可能是不可取的。

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)

尽管在任何一种情况下,如果未提供原始数据,JSON_OBJECT 函数调用都会清空嵌套对象的属性值。JSON_SET但是从上一个查询中可以看出,$.test1的定义中没有提供attributes,并且它保持不变,因此可以从查询中省略那些未修改的属性。

于 2016-07-12T13:28:47.547 回答
6

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

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

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

于 2020-07-22T13:20:33.617 回答
3

Fyrye,谢谢你的遮阳篷,非常感谢!由于数据没有固定的结构并且每条记录都可能不同,我需要一个解决方案,我可以生成一个查询,该查询将在单个查询中自动生成整个 JSON 对象。

我真的很喜欢您使用该JSON_SET(attributes, "$.test2", IFNULL(attributes->'$.test2',JSON_OBJECT()))方法的解决方案。因为我继续搜索,所以我自己也想出了一个使用JSON_MERGE函数的解决方案。

当我执行更新时,我正在使用JSON_MERGE将一个空的 JSON 对象合并到数据库中的字段上,对于所有带有子节点的键,因此在数据库中的 JSON 字段中可用,之后,使用JSON_SET更新值。所以完整的查询如下所示:

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

执行此查询后,结果将如下所示:

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

我不知道目前哪种方法更好,现在都可以。将来会进行一些速度测试,以检查它们在 1 更新 10.000 行时的表现!

于 2016-07-14T08:34:40.083 回答
0

在像你们中的许多人一样到处搜索之后,我找到了这里列出的最佳解决方案:https ://forums.mysql.com/read.php?20,647956,647969#msg-647969

从站点:他节点和子节点,但不包含任何数据......所以在上面的例子中,对象将是:

{"nodes": {}} 

执行更新时,我使用JSON_MERGE将空 JSON 对象合并到数据库中的字段上,因此所有节点/子节点在 te 数据库中的 JSON 字段中可用,然后JSON_SET用于更新值。所以完整的查询如下所示:

UPDATE table SET attributes = JSON_MERGE(attributes, '{"nodes": {}'), attributes = JSON_SET(attributes, "$.nodes.node2", "Node 2") 

目前,这是可行的。但这是一个奇怪的解决方法。也许这可以在以前的 MySQL 版本中查看,所以JSON_SET在设置子节点时也会创建父节点?

于 2018-11-08T20:07:36.447 回答