如何使用 mysql 本机 json 函数生成嵌套的 json 对象?
Posted
技术标签:
【中文标题】如何使用 mysql 本机 json 函数生成嵌套的 json 对象?【英文标题】:How do I generate nested json objects using mysql native json functions? 【发布时间】:2016-09-25 01:17:28 【问题描述】:在 mysql 5.7.12 版(手册中的第 13.16 节)中仅使用本机 JSON 函数(无 php 等)我正在尝试编写一个查询以从包含子对象的关系表生成 JSON 文档。举个例子:
CREATE TABLE `parent_table` (
`id` int(11) NOT NULL,
`desc` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `child_table` (
`id` int(11) NOT NULL,
`parent_id` int(11) NOT NULL,
`desc` varchar(20) NOT NULL,
PRIMARY KEY (`id`,`parent_id`)
);
insert `parent_table` values (1,'parent row 1');
insert `child_table` values (1,1,'child row 1');
insert `child_table` values (2,1,'child row 2');
我正在尝试生成一个如下所示的 JSON 文档:
[
"id" : 1,
"desc" : "parent row 1",
"child_objects" : [
"id" : 1,
"parent_id" : 1,
"desc" : "child row 1"
,
"id" : 2,
"parent_id" : 1,
"desc" : "child row 2"
]
]
我是 MySQL 新手,我怀疑有一种 SQL 模式可用于从一对多关系生成嵌套 JSON 对象,但我找不到它。
在 Microsoft SQL(我更熟悉)中,以下工作:
select
[p].[id]
,[p].[desc]
,(select * from [dbo].[child_table] where [parent_id] = [p].[id] for json auto) AS [child_objects]
from [dbo].[parent_table] [p]
for json path
我尝试在 MySQL 中编写如下等效代码:
select json_object(
'id',p.id
,'desc',p.`desc`
,'child_objects',(select json_object('id',id,'parent_id',parent_id,'desc',`desc`)
from child_table where parent_id = p.id)
)
from parent_table p;
select json_object(
'id',p.id
,'desc',p.`desc`
,'child_objects',json_array((select json_object('id',id,'parent_id',parent_id,'desc',`desc`)
from child_table where parent_id = p.id))
)
from parent_table p
两次尝试都失败并出现以下错误:
Error Code: 1242. Subquery returns more than 1 row
【问题讨论】:
【参考方案1】:您收到这些错误的原因是父 json 对象不希望将结果集作为其输入之一,您需要有简单的对象对,例如 name, string 等 bug report - may be available in future functionality... 这只是意味着您需要将多行结果转换为以逗号分隔的结果串联,然后转换为 json 数组。
你的第二个例子几乎已经完成了。
您可以使用 GROUP_CONCAT 函数实现您的目标
select json_object(
'id',p.id
,'desc',p.`desc`
,'child_objects',json_array(
(select GROUP_CONCAT(
json_object('id',id,'parent_id',parent_id,'desc',`desc`)
)
from child_table
where parent_id = p.id))
)
from parent_table p;
这几乎可行,它最终将子查询视为一个字符串,将转义字符留在其中。
'\"id\": 1,
\"desc\": \"parent row 1\",
\"child_objects\":
[\"
\\\"id\\\": 1,
\\\"desc\\\": \\\"child row 1\\\",
\\\"parent_id\\\": 1
,
\\\"id\\\": 2,
\\\"desc\\\": \\\"child row 2\\\",
\\\"parent_id\\\": 1\"
]
'
为了使其以适当的格式工作,您需要更改创建 JSON 输出的方式,如下所示:
select json_object(
'id',p.id
,'desc',p.`desc`
,'child_objects',(select CAST(CONCAT('[',
GROUP_CONCAT(
JSON_OBJECT(
'id',id,'parent_id',parent_id,'desc',`desc`)),
']')
AS JSON) from child_table where parent_id = p.id)
) from parent_table p;
这将为您提供所需的确切结果:
'\"id\": 1,
\"desc\": \"parent row 1\",
\"child_objects\":
[\"id\": 1,
\"desc\": \"child row 1\",
\"parent_id\": 1
,
\"id\": 2,
\"desc\": \"child row 2\",
\"parent_id\": 1
]
'
【讨论】:
你好,我无法使用 json_object 这个函数。 #1305 - 函数 json_object 不存在。怎么办??? 查看你使用的是哪个版本的Mysql。旧版本不支持 json_object 当前版本是5.6 JSON 聚合函数JSON_ARRAYAGG()
和 JSON_OBJECTAGG()
现在在 MySQL 8 中可用 mysqlserverteam.com/mysql-8-0-labs-json-aggregation-functions
这很好用,但是只有两个级别,我们可以到达'n'级别【参考方案2】:
对于 MariaDb,CAST AS JSON 不起作用。但 JSON_EXTRACT 可用于将字符串转换为 JSON 对象:
select json_object(
'id',p.id
,'desc',p.`desc`
,'child_objects',JSON_EXTRACT(IFNULL((select
CONCAT('[',GROUP_CONCAT(
json_object('id',id,'parent_id',parent_id,'desc',`desc`)
),']')
from child_table where parent_id = p.id),'[]'),'$')
) from parent_table p;
【讨论】:
【参考方案3】:我尝试了 group_concat 解决方案,但由于 group_concat 限制 (group_concat_max_len),我发现它的问题是更大的字符串。 我编写的新函数解决了将字符串转换为 JSON 对象的问题,如下所示以及如何使用它。 在 MariaDB 10.5.12 上测试
用法:https://i.stack.imgur.com/cWfd7.jpg
CREATE FUNCTION `ut_tf_array`(input_json longtext) RETURNS longtext CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci
COMMENT 'Function for transform json array agg'
BEGIN
DECLARE transformed_data_list longtext ;
DECLARE record longtext ;
DECLARE i_count int ;
DECLARE i_count_items int ;
SET i_count = 0;
SET i_count_items = JSON_LENGTH(JSON_EXTRACT(input_json,'$'));
SET transformed_data_list = '[]';
-- return array with length = zero
IF input_json is NULL THEN
RETURN transformed_data_list;
END IF;
WHILE i_count < i_count_items DO
-- fetch into record
SELECT JSON_EXTRACT( JSON_EXTRACT( input_json ,'$') , CONCAT('$[',i_count,']')) INTO record;
-- append to transformed_data_list
SELECT JSON_ARRAY_APPEND(transformed_data_list, '$', JSON_EXTRACT(record, '$')) into transformed_data_list;
SET i_count := i_count + 1;
END WHILE;
-- done
RETURN transformed_data_list;
END
【讨论】:
以上是关于如何使用 mysql 本机 json 函数生成嵌套的 json 对象?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Spring boot 和 MYSQL 为多级菜单列表创建嵌套 JSON?
如何使用 laravel 或仅使用本机 php 更新来自 mysql 数据库的 JSON 数据?
如何使用 Python 将嵌套字典/json 插入 MySQL 表