将MySQL中的JSON数组转换为行

Chr*_*nes 21 mysql json database-normalization

更新:现在可以通过JSON_TABLE函数在MySQL 8中实现:https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html

我喜欢MySQL 5.7中的新JSON函数,但是试图将JSON中的值合并到一个普通的表结构中.

从中获取JSON,操作和提取数组等很简单.一路JSON_EXTRACT.但是从JSON数组到行的逆转呢?也许我对现有的MySQL JSON功能非常密切,但我还是无法解决这个问题.

例如,假设我有一个JSON数组,并希望为数组中的每个元素插入一行及其值?我找到的唯一方法是编写一堆JSON_EXTRACT(...'$ [0]')JSON_EXTRACT(...'$ [1]')等并将它们组合在一起.

或者,说我有一个JSON数组,并希望GROUP_CONCAT()它到一个逗号分隔的字符串?

换句话说,我知道我可以这样做:

SET @j = '[1, 2, 3]';
SELECT GROUP_CONCAT(JSON_EXTRACT(@j, CONCAT('$[', x.n, ']'))) AS val
  FROM   
  (    
    SELECT 0 AS n    
    UNION    
    SELECT 1 AS n    
    UNION    
    SELECT 2 AS n    
    UNION    
    SELECT 3 AS n    
    UNION    
    SELECT 4 AS n    
    UNION    
    SELECT 5 AS n    
  ) x
WHERE x.n < JSON_LENGTH(@j);
Run Code Online (Sandbox Code Playgroud)

但这伤害了我的眼睛.而我的心.

我该怎么做:

SET @j = '[1, 2, 3]';
SELECT GROUP_CONCAT(JSON_EXTRACT(@j, '$[ * ]'))
Run Code Online (Sandbox Code Playgroud)

...并将它与数组中的值和JSON数组本身连接在一起?

我想我在这里寻找的是某种JSON_SPLIT:

SET @j = '[1, 2, 3]';

SELECT GROUP_CONCAT(val)
FROM
  JSON_SPLIT(JSON_EXTRACT(@j, '$[ * ]'), '$')
Run Code Online (Sandbox Code Playgroud)

如果MySQL有一个正确的STRING_SPLIT(val,'separator')表返回函数,我可以破解它(逃避被诅咒),但这也不可用.

小智 28

确实,将规范化为JSON并不是一个好主意,但有时您需要处理JSON数据,并且有一种方法可以将JSON数组提取到查询中的行中.

诀窍是在临时或内联索引表上执行连接,这为JSON数组中的每个非空值提供了一行.即,如果你有一个值为0,1和2的表,你加入一个带有两个条目的JSON数组"fish",那么fish [0]匹配0,结果是一行,fish [1]匹配1,导致第二行,但fish [2]为null,因此它与2不匹配,并且在连接中不产生行.索引表中需要的数字与JSON数据中任何数组的最大长度一样多.这有点像黑客,它和OP的例子一样痛苦,但它非常方便.

示例(需要MySQL 5.7.8或更高版本):

CREATE TABLE t1 (rec_num INT, jdoc JSON);
INSERT INTO t1 VALUES 
  (1, '{"fish": ["red", "blue"]}'), 
  (2, '{"fish": ["one", "two", "three"]}');

SELECT
  rec_num,
  idx,
  JSON_EXTRACT(jdoc, CONCAT('$.fish[', idx, ']')) AS fishes
FROM t1
  -- Inline table of sequential values to index into JSON array
JOIN ( 
  SELECT  0 AS idx UNION
  SELECT  1 AS idx UNION
  SELECT  2 AS idx UNION
  -- ... continue as needed to max length of JSON array
  SELECT  3
  ) AS indexes
WHERE JSON_EXTRACT(jdoc, CONCAT('$.fish[', idx, ']')) IS NOT NULL
ORDER BY rec_num, idx;
Run Code Online (Sandbox Code Playgroud)

结果是:

+---------+-----+---------+
| rec_num | idx | fishes  |
+---------+-----+---------+
|       1 |   0 | "red"   |
|       1 |   1 | "blue"  |
|       2 |   0 | "one"   |
|       2 |   1 | "two"   |
|       2 |   2 | "three" |
+---------+-----+---------+
Run Code Online (Sandbox Code Playgroud)

看起来MySQL团队可能会在MySQL 8中添加一个JSON_TABLE函数来使这一切变得更容易.(http://mysqlserverteam.com/mysql-8-0-labs-json-aggregation-functions/)


小智 8

在2018年。我要为这种情况做些什么。

  1. 准备一个表,该表的行必须连续不断。

    CREATE TABLE `t_list_row` (
    `_row` int(10) unsigned NOT NULL,
    PRIMARY KEY (`_row`)
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
    
    INSERT t_list_row VALUES (0), (1), (2) .... (65535) big enough;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将来可以在行中使用简单的JSON数组。

    SET @j = '[1, 2, 3]';
    SELECT 
    JSON_EXTRACT(@j, CONCAT('$[', B._row, ']'))
    FROM (SELECT @j AS B) AS A
    INNER JOIN t_list_row AS B ON B._row < JSON_LENGTH(@j);
    
    Run Code Online (Sandbox Code Playgroud)

这样 就像“克里斯·海恩斯”那样。但是您不需要知道数组大小。

良好:清晰,简短的代码,无需知道数组大小,无需循环,无需调用其他函数将很快。

坏:您还需要一个具有足够行的表。

  • 以下是用于生成不同“1000”、“10000”和“100000”大小的这些“t_list_row”表的 SQL 查询:https://gist.github.com/milosb793/812d5e7c33a0bfd37ed2c3dcad0cea1c (2认同)

Chr*_*nes 7

这是在MySQL 8+中使用JSON_TABLE的方法

SELECT *
     FROM
       JSON_TABLE(
         '[5, 6, 7]',
         "$[*]"
         COLUMNS(
           Value INT PATH "$"
         )
       ) data;
Run Code Online (Sandbox Code Playgroud)

您还可以通过将带分隔符的字符串并将其转换为JSON字符串,将其用作MySQL否则缺少的常规字符串拆分函数(类似于PG的regexp_split_to_table或MSSQL的STRING_SPLIT):

set @delimited = 'a,b,c';

SELECT *
     FROM
       JSON_TABLE(
         CONCAT('["', REPLACE(@delimited, ',', '", "'), '"]'),
         "$[*]"
         COLUMNS(
           Value varchar(50) PATH "$"
         )
       ) data;
Run Code Online (Sandbox Code Playgroud)