如何使用 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 对象?的主要内容,如果未能解决你的问题,请参考以下文章

使用 php mysql 创建嵌套 json

如何使用 Spring boot 和 MYSQL 为多级菜单列表创建嵌套 JSON?

如何使用 laravel 或仅使用本机 php 更新来自 mysql 数据库的 JSON 数据?

如何使用 Python 将嵌套字典/json 插入 MySQL 表

如何使用 laravel 或 mysql 创建嵌套的 json?

如何使用 pyspark 在 aws 胶水中展平嵌套 json 中的数组?