MySQL 5.7+,JSON_SET 嵌套路径中的值
MySQL 5.7+, JSON_SET value in nested path
对于最近的开发项目,我们使用 MySQL 5.7,因此我们可以利用最新的 JSON 功能...
我正在构建一个更新查询,其中嵌套的 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-object,如
{"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-对象,添加完整路径...如何做到这一点?
从 MySQL 版本 5.7.13 开始,假设您想要
的最终结果
{"test1": "Test 1", "test2": {"test3": "Test 3"}}
在您的示例中,正在更新的 attributes
列设置为 {"test1": "Test 1"}
查看您最初的 UPDATE
查询,我们可以看到 $.test2.test3
不存在。
所以不能设置为
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可以加$.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 点符号,而是需要明确告诉 MySQL 该密钥作为 JSON 对象存在。
这类似于 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
查询可以看出,在attributes
的定义中没有提供$.test1
,它保持原样,所以那些没有修改的属性可以从查询中省略.
Fyye,谢谢你的遮阳篷,非常感谢!由于数据没有固定的结构并且每条记录都可能不同,我需要一个解决方案,我可以在其中生成一个查询,该查询将在单个查询中自动生成总的 JSON-object。
我非常喜欢您使用 JSON_SET(attributes, "$.test2", IFNULL(attributes->'$.test2',JSON_OBJECT()))
方法的解决方案。因为我继续搜索,所以我自己也使用 JSON_MERGE
函数找到了解决方案。
当我执行更新时,我正在使用 JSON_MERGE
将一个空的 JSON-object 合并到数据库中的字段上,对于所有带有子节点的键,所以在数据库的 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 行时它们是如何执行的!
像你们中的许多人一样到处搜索后,我找到了这里列出的最佳解决方案:https://forums.mysql.com/read.php?20,647956,647969#msg-647969
来自网站:
他节点和子节点,但不包含任何数据......
所以在上面的例子中,对象将是这样的:
{"nodes": {}}
执行更新时,我正在使用 JSON_MERGE
将空的 JSON-对象合并到数据库中的字段中,因此所有节点/子节点在 JSON-te 数据库中的字段,然后使用 JSON_SET
更新值。所以完整的查询看起来像这样:
UPDATE table SET attributes = JSON_MERGE(attributes, '{"nodes": {}'), attributes = JSON_SET(attributes, "$.nodes.node2", "Node 2")
目前,这是可行的。
但这是一个奇怪的解决方法。也许这可以在以前的 MySQL 版本中查看,所以 JSON_SET
在设置子节点时也会创建父节点?
现在,从 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": {}}}
的预期结果。
对于最近的开发项目,我们使用 MySQL 5.7,因此我们可以利用最新的 JSON 功能...
我正在构建一个更新查询,其中嵌套的 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-object,如
{"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-对象,添加完整路径...如何做到这一点?
从 MySQL 版本 5.7.13 开始,假设您想要
的最终结果{"test1": "Test 1", "test2": {"test3": "Test 3"}}
在您的示例中,正在更新的 attributes
列设置为 {"test1": "Test 1"}
查看您最初的 UPDATE
查询,我们可以看到 $.test2.test3
不存在。
所以不能设置为
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可以加$.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 点符号,而是需要明确告诉 MySQL 该密钥作为 JSON 对象存在。
这类似于 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
查询可以看出,在attributes
的定义中没有提供$.test1
,它保持原样,所以那些没有修改的属性可以从查询中省略.
Fyye,谢谢你的遮阳篷,非常感谢!由于数据没有固定的结构并且每条记录都可能不同,我需要一个解决方案,我可以在其中生成一个查询,该查询将在单个查询中自动生成总的 JSON-object。
我非常喜欢您使用 JSON_SET(attributes, "$.test2", IFNULL(attributes->'$.test2',JSON_OBJECT()))
方法的解决方案。因为我继续搜索,所以我自己也使用 JSON_MERGE
函数找到了解决方案。
当我执行更新时,我正在使用 JSON_MERGE
将一个空的 JSON-object 合并到数据库中的字段上,对于所有带有子节点的键,所以在数据库的 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 行时它们是如何执行的!
像你们中的许多人一样到处搜索后,我找到了这里列出的最佳解决方案:https://forums.mysql.com/read.php?20,647956,647969#msg-647969
来自网站: 他节点和子节点,但不包含任何数据...... 所以在上面的例子中,对象将是这样的:
{"nodes": {}}
执行更新时,我正在使用 JSON_MERGE
将空的 JSON-对象合并到数据库中的字段中,因此所有节点/子节点在 JSON-te 数据库中的字段,然后使用 JSON_SET
更新值。所以完整的查询看起来像这样:
UPDATE table SET attributes = JSON_MERGE(attributes, '{"nodes": {}'), attributes = JSON_SET(attributes, "$.nodes.node2", "Node 2")
目前,这是可行的。
但这是一个奇怪的解决方法。也许这可以在以前的 MySQL 版本中查看,所以 JSON_SET
在设置子节点时也会创建父节点?
现在,从 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": {}}}
的预期结果。