对于最近的开发项目,我们使用的是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"
)
当我执行此查询时,attributes-field包含数据
{"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对象,添加完整路径......如何做到这一点?
答案 0 :(得分:6)
假设你想要
的最终结果attributes
在您的示例中,正在更新的{"test1": "Test 1"}
列设置为UPDATE
查看您的初始$.test2.test3
查询,我们可以看到$.test2
不存在。
所以它不能设置为
JSON_SET()在JSON文档中插入或更新数据并返回 结果。如果任何参数为NULL或路径(如果给定),则返回NULL 找不到对象。
含义MySQL可以添加$.test2
,但由于$.test2.test3
不是对象,MySQL无法添加到$.test2
。
因此,您需要通过执行以下操作将mysql> SELECT * FROM testing;
+----+---------------------+
| id | attributes |
+----+---------------------+
| 1 | {"test1": "Test 1"} |
+----+---------------------+
1 row in set (0.00 sec)
定义为json对象。
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)
$a = (object) ['test1' => 'Test 1'];
$a->test2->test3 = 'Test 3';
//PHP Warning: Creating default object from empty value
因此,您需要明确告诉MySQL密钥作为JSON对象存在,而不是依赖于MySQL点表示法。
这类似于PHP也定义了不存在的对象属性值。
$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_SET
尽管在任何一种情况下都没有提供原始数据,但JSON_OBJECT函数调用将清空嵌套对象的属性值。但正如您从上一个$.test1
查询中看到的那样,attributes
的定义中未提供click
,并且它保持不变,因此可以从查询中省略未修改的属性
答案 1 :(得分:2)
Fyrye,感谢awnser,非常感谢!由于数据没有固定的结构,并且每个记录可能不同,我需要一个解决方案,我可以生成一个查询,在一个查询中自动生成总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行时的预测形式!
答案 2 :(得分:2)
现在,从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": {}}}
的预期结果。
答案 3 :(得分: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
还会在设置子节点时创建父节点?