简体   繁体   English

如何使用 mysql 原生 json 函数生成嵌套的 json 对象?

[英]How do I generate nested json objects using mysql native json functions?

Using only the native JSON fuctions (no PHP, etc) in MySQL version 5.7.12 (section 13.16 in the manual) I am trying to write a query to generate a JSON document from relational tables that contains a sub object. Using only the native JSON fuctions (no PHP, etc) in MySQL version 5.7.12 (section 13.16 in the manual) I am trying to write a query to generate a JSON document from relational tables that contains a sub object. Given the following example:给定以下示例:

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');

I am trying to generate a JSON document that looks like this:我正在尝试生成如下所示的 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"
        }
    ]
}]

I am new to MySQL and suspect there is a SQL pattern for generating nested JSON objects from one to many relationships but I'm having trouble finding it.我是 MySQL 的新手,怀疑有一个 SQL 模式用于从一对多关系生成嵌套的 JSON 对象,但我很难找到它。

In Microsoft SQL (which I'm more familiar with) the following works:在 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

I attempted to write the equivalent in MySQL as follows:我尝试在 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

Both attempts fail with the following error:两次尝试都失败并出现以下错误:

Error Code: 1242. Subquery returns more than 1 row

The reason you are getting these errors is that the parent json object is not expecting a result set as one of its inputs, you need to have simple object pairs like {name, string} etc bug report - may be available in future functionality ... this just means that you need to convert your multi row results into a concatination of results separated by commas and then converted into a json array.您收到这些错误的原因是父 json 对象不希望将结果集作为其输入之一,您需要有简单的对象对,如 {name, string} 等错误报告 - 可能在未来的功能中可用.. . 这只是意味着您需要将多行结果转换为以逗号分隔的结果的串联,然后转换为 json 数组。

You almost had it with your second example.你的第二个例子几乎已经完成了。

You can achieve what you are after with the GROUP_CONCAT function您可以使用 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;

This almost works, it ends up treating the subquery as a string which leaves the escape characters in there.这几乎可行,它最终将子查询视为一个字符串,将转义字符留在其中。

'{\"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}\"
    ]
}'

In order to get this working in an appropriate format, you need to change the way you create the JSON output as follows:为了使其以适当的格式工作,您需要更改创建 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;

This will give you the exact result you require:这将为您提供所需的确切结果:

'{\"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
    }]  
}'

For MariaDb, CAST AS JSON does not work.对于 MariaDb,CAST AS JSON 不起作用。 But JSON_EXTRACT may be used to convert a string to a JSON object:但 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;

I tried group_concat solution but I found it's problems larger string because of group_concat limitations (group_concat_max_len).我尝试了 group_concat 解决方案,但由于 group_concat 限制(group_concat_max_len),我发现它的问题是更大的字符串。 I wrote the new function resolve the problem about converting a string to JSON object as bellow and how to use it.我编写的新函数解决了将字符串转换为 JSON 对象的问题,如下所示以及如何使用它。 Tested on MariaDB 10.5.12在 MariaDB 10.5.12 上测试

Usage: https://i.stack.imgur.com/cWfd7.jpg用法: 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

Below Query works for me.下面的查询对我有用。

SELECT JSON_ARRAYAGG(JSON_OBJECT('Id', p.id, 'desc', p.`desc`, 'child_objects', temp_json)) AS json_value
FROM (
SELECT p.id, p.`desc`,
JSON_ARRAYAGG(JSON_OBJECT('id', p.id, 'parent_id', p.parent_id, 'desc', p.`desc`)) AS temp_json
FROM parent_table p
GROUP BY p.id
) t;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM