Mysql展开数组

q8l4jmvw  于 2023-11-16  发布在  Mysql
关注(0)|答案(2)|浏览(135)

该表包含json数据,我想从这个json中提取labelKey
表:d_json
| 数据|
| --|
| {“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”]|
但是我希望结果是平面的,每行包含一个元素而不是数组,例如
| 结果|
| --|
| “key10”|
| “key11”|
| “key21”|
| “key22”|
| “key23”|
没有得到任何线索如何扁平化结果数组

egdjgwm8

egdjgwm81#

在mysql v8+上,你可以使用JSON_TABLE函数这样做:

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

字符串
测试结果:
| labelKey|
| --|
| 关键10|
| 关键11|
| 关键20|
| 关键21|
| 钥匙22|
这里有一个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

kmbjn2e3

kmbjn2e32#

不知道你可以在MySQL的最早版本中做到这一点,但这对我来说在5. 7中是有效的。我使用JSON_ARRAYAGG来合并组合JSON数组,然后使用JSON_EXTRACT和$[*][*]来扁平化它们。

SELECT JSON_EXTRACT(
  JSON_ARRAYAGG(
    JSON_EXTRACT(
      JSON_EXTRACT(
        j.data,'$.options'),'$[*].labelKey'
      )
    ),
  "$[*][*]"
) FROM d_json j AS result;

字符串

相关问题