【问题标题】:Mysql Flatten an arrayMysql 展平一个数组
【发布时间】:2021-10-24 06:12:48
【问题描述】:

该表包含 json 数据,我想从该 json 中提取 labelKey

表:d_json

data
{"tag":null,"options":[{"labelKey":"key10","value":"yes","selected":true},{"labelKey":"key11","value":"no","selected":false}]}
{"tag":null,"options":[{"labelKey":"key20","value":"yes","selected":true},{"labelKey":"key21","value":"no","selected":false},{"labelKey":"key22","value":"no","selected":false}]}

我使用以下查询来提取“labelKey”

SELECT JSON_EXTRACT(JSON_EXTRACT(j.data,'$.options'),'$[*].labelKey') FROM d_json j AS 结果;

返回如下结果

result
["key10", "key12"]
["key20", "key21", "key22"]

但是我希望结果是平的,每一行包含一个元素而不是数组,例如

result
"key10"
"key11"
"key21"
"key22"
"key23"

不知道如何展平结果数组

【问题讨论】:

    标签: mysql arrays flatten


    【解决方案1】:

    在 mysql v8+ 上,您可以使用 JSON_TABLE 函数来做到这一点,如下所示:

    SELECT p.*
    FROM d_json, 
         JSON_TABLE(data, '$.options[*]' COLUMNS (
                    labelKey VARCHAR(40) PATH '$.labelKey')
         ) p;
    

    结果:

    labelKey
    key10
    key11
    key20
    key21
    key22

    这是demo fiddle

    编辑:

    在旧的 MySQL 版本上,试试这个:

    SELECT TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(val2,',',rn),',',-1))
    FROM (SELECT 1 rn UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS r
    CROSS JOIN
      (SELECT REPLACE(REPLACE(GROUP_CONCAT(val),'[',''),']','') AS val2
         FROM 
           (SELECT JSON_EXTRACT(JSON_EXTRACT(j.data,'$.options'),'$[*].labelKey') AS val
          FROM d_json j) v1 
        ) v2;
    

    Demo fiddle

    这个想法是 CROSS JOIN 使用一系列行号,然后使用相同的序列从 GROUP_CONCAT 使用 SUBSTRING_INDEX 的值中提取。在上面的查询示例中,我使用了以下形式的硬编码行序列:

    (SELECT 1 rn UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS r
    

    理想情况下,最好的方法是找出所需的序列并动态生成。

    更新:

    在旧的 MySQL 版本上生成编号序列是一个挑战,尤其是当我们的目标是动态生成时。有一种方法不是动态的,但是可以从一个很长的查询中生成一个很大的编号序列,但是如果你打算长时间使用这个序列,我建议你为它创建一个表:

    CREATE TABLE number_seq (
    sequences INT);
    
    INSERT INTO number_seq
    SELECT @row := @row + 1 AS rn FROM 
    (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION 
    SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1 CROSS JOIN
    (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION 
    SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2 CROSS JOIN
    (SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION 
    SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3 CROSS JOIN
    (SELECT @row:=0) numbers;
    

    上面的查询将生成 1-1000 范围内的数字并插入到表中。获得该表后,您只需要像这样编写查询:

    SELECT TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(val2,',',sequences),',',-1))
    FROM (SELECT sequences FROM 
    (SELECT (LENGTH(val2)-LENGTH(REPLACE(val2,',','')))+1 AS valLen FROM
    (SELECT REPLACE(REPLACE(GROUP_CONCAT(val),'[',''),']','') AS val2 FROM 
           (SELECT JSON_EXTRACT(JSON_EXTRACT(j.data,'$.options'),'$[*].labelKey') AS val
          FROM d_json j) v1 
        ) v2 ) v3 JOIN number_seq t ON sequences <= valLen) r
    CROSS JOIN
      (SELECT REPLACE(REPLACE(GROUP_CONCAT(val),'[',''),']','') AS val2
         FROM 
           (SELECT JSON_EXTRACT(JSON_EXTRACT(j.data,'$.options'),'$[*].labelKey') AS val
          FROM d_json j) v1 
        ) v2;
    

    与上一个查询相比,变化的亮点是硬编码编号序列与查询之间的切换,该查询基本上在最终的JSON_EXTRACT 中获取用逗号分隔的总值,并将其与创建的number_seq 表连接以获得需要的行。这部分在这里:

    SELECT sequences FROM 
    (SELECT (LENGTH(val2)-LENGTH(REPLACE(val2,',','')))+1 AS valLen FROM
    (SELECT REPLACE(REPLACE(GROUP_CONCAT(val),'[',''),']','') AS val2 FROM 
           (SELECT JSON_EXTRACT(JSON_EXTRACT(j.data,'$.options'),'$[*].labelKey') AS val
          FROM d_json j) v1 
        ) v2 ) v3 JOIN number_seq t ON sequences <= valLen
    

    这是一个更新的小提琴供参考https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=ace8babce8d7bbb97f7e016a754e93a9

    【讨论】:

    • 谢谢,但是“Json_table”适用于 mysql 8+,但我使用的是 5.7.33 版本 :(,是否有另一种适用于旧版本的解决方案
    • 谢谢@FaNo_FN 它有帮助,但它硬编码为 5 结果。知道如何动态生成序列
    • 这是旧 MySQL 的限制。如果您使用的是支持窗口功能的版本,如WITH RECURSIVE,无论是 MySQL 还是 MariaDB,创建序列将非常容易。我能想到的唯一选择是生成一系列编号序列并使用LENGTH() 函数来获取总值并将其添加到WHERE 过滤器中......但这会使查询更长而且它仍然是硬编码方法。我会在我的答案中更新,供您尝试。
    • 感谢您提供详细信息。期待后续更新
    • 我已经更新了答案@IjazAhmed。虽然我没有在更新中包含编号行序列查询,但您可以在更新的小提琴链接中找到一个示例。
    猜你喜欢
    • 2019-11-17
    • 2014-08-29
    • 2014-08-19
    • 2018-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多