Mysql 展平数组
Mysql Flatten an array
table 包含 json 数据,我想从这个 json
中提取 labelKey
table: 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;
returns结果如下
result
["key10", "key12"]
["key20", "key21", "key22"]
但是我想要结果是扁平的,每一行包含一个元素而不是数组,例如
result
"key10"
"key11"
"key21"
"key22"
"key23"
不知道如何展平结果数组
在 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;
我们的想法是 CROSS JOIN
使用一系列行号,然后使用相同的序列使用 SUBSTRING_INDEX
从 GROUP_CONCAT
值中提取值。在上面的查询示例中,我使用以下形式的硬编码行序列:
(SELECT 1 rn UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS r
理想情况下,最好的方法是找出所需的序列并动态生成它。
更新:
在旧 MySQL 版本上生成编号序列是一个挑战,尤其是当我们的目标是动态生成时。有一种方法不是动态的,但可以从一个很长的查询中生成一个大的编号序列,但如果你打算长期使用这个序列,我建议你只为它创建一个 table :
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 范围内的数字并插入 table。一旦你有了 table,你只需要像这样写你的查询:
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
table 获取所需的行。这部分在这里:
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
这是更新后的 fiddle 供参考 https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=ace8babce8d7bbb97f7e016a754e93a9
table 包含 json 数据,我想从这个 json
中提取 labelKeytable: 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;
returns结果如下
result |
---|
["key10", "key12"] |
["key20", "key21", "key22"] |
但是我想要结果是扁平的,每一行包含一个元素而不是数组,例如
result |
---|
"key10" |
"key11" |
"key21" |
"key22" |
"key23" |
不知道如何展平结果数组
在 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;
我们的想法是 CROSS JOIN
使用一系列行号,然后使用相同的序列使用 SUBSTRING_INDEX
从 GROUP_CONCAT
值中提取值。在上面的查询示例中,我使用以下形式的硬编码行序列:
(SELECT 1 rn UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS r
理想情况下,最好的方法是找出所需的序列并动态生成它。
更新:
在旧 MySQL 版本上生成编号序列是一个挑战,尤其是当我们的目标是动态生成时。有一种方法不是动态的,但可以从一个很长的查询中生成一个大的编号序列,但如果你打算长期使用这个序列,我建议你只为它创建一个 table :
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 范围内的数字并插入 table。一旦你有了 table,你只需要像这样写你的查询:
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
table 获取所需的行。这部分在这里:
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
这是更新后的 fiddle 供参考 https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=ace8babce8d7bbb97f7e016a754e93a9