将具有未知键的 JSON 扩展到具有 MySQL JSON_TABLE 的行
Expand JSON with unknown keys to rows with MySQL JSON_TABLE
我有一个 MySQL 8.0.22 JSON 列,其中包含具有事先不知道的键的对象:
'{"x": 1, "y": 2, "z": 3}'
'{"e": 4, "k": 5}'
我想使用 JSON_TABLE
将这些值扩展为包含键值对的多行:
key
value
x
1
y
2
z
3
e
4
k
5
困难当然在于密钥先验是未知的。我想到的最好的事情是...
SET @json_doc = '{"x": 1, "y": 2, "z": 3}';
SELECT a.seq, b.k, a.v
FROM
JSON_TABLE(
@json_doc,
"$.*"
COLUMNS(
seq FOR ordinality,
v INT PATH "$"
)
) AS a,
JSON_TABLE(
JSON_KEYS(@json_doc),
"$[*]"
COLUMNS(
seq FOR ordinality,
k CHAR(1) PATH "$"
)
) AS b
WHERE a.seq = b.seq;
这感觉很奇怪,因为它使用两个 JSON_TABLE
调用,对值和键进行交叉连接,然后保持对齐。我想找到一个像这样更简单的查询...
SELECT a.seq, b.k, a.v
FROM
JSON_TABLE(
@json_doc,
"$.*"
COLUMNS(
seq FOR ordinality,
k CHAR(1) PATH "?" -- <-- what do I put here to find each key?
v INT PATH "$"
)
) AS a,
我知道这个问题可能可以通过 CTE 或数字 table 和 JSON_EXTRACT
来解决。但是,如果可能的话,我想找到一些高性能和可读的东西。
您可以通过使用 ROW_NUMBER()
window 函数来使用枚举,同时通过使用 JSON_KEYS()
确定键值,然后使用 JSON_EXTRACT()
从中提取相应的键我们得到的数组如
WITH k AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY `jsdata` ORDER BY value DESC) AS rn,
JSON_KEYS(`jsdata`) AS jk
FROM `tab` AS t
JOIN JSON_TABLE(`jsdata`,'$.*' COLUMNS (value INT PATH '$')) j
)
SELECT JSON_UNQUOTE(JSON_EXTRACT(jk, CONCAT('$[',rn-1,']'))) AS "key",
value
FROM k
或使用以下查询更直接
SELECT JSON_UNQUOTE(
JSON_EXTRACT(JSON_KEYS(`jsdata`),
CONCAT('$[',
ROW_NUMBER() OVER(PARTITION BY `jsdata` ORDER BY value DESC)-1,
']'))
) AS "key", value
FROM `tab` AS t
JOIN JSON_TABLE(`jsdata`,'$.*' COLUMNS (value INT PATH '$')) j
尝试在获得 JSON_KEYS
作为行后直接执行 JSON_EXTRACT
:
WITH j AS (
SELECT CAST('{"a": 1, "b": "-1", "c": null}' AS JSON) o UNION ALL
SELECT CAST('{"x": 2, "y": "-2", "z": null}' AS JSON)
)
SELECT k, JSON_EXTRACT(j.o, CONCAT('$."', jt.k, '"')) v
FROM j
, JSON_TABLE(JSON_KEYS(o), '$[*]' COLUMNS (k VARCHAR(200) PATH '$')) jt;
Barbaros 的答案可以使用您提供的演示数据解决您的问题,但如果您的 json 对象在不同键下具有相同的值,则可能无法得到您想要的结果。
我有一个 MySQL 8.0.22 JSON 列,其中包含具有事先不知道的键的对象:
'{"x": 1, "y": 2, "z": 3}'
'{"e": 4, "k": 5}'
我想使用 JSON_TABLE
将这些值扩展为包含键值对的多行:
key | value |
---|---|
x | 1 |
y | 2 |
z | 3 |
e | 4 |
k | 5 |
困难当然在于密钥先验是未知的。我想到的最好的事情是...
SET @json_doc = '{"x": 1, "y": 2, "z": 3}';
SELECT a.seq, b.k, a.v
FROM
JSON_TABLE(
@json_doc,
"$.*"
COLUMNS(
seq FOR ordinality,
v INT PATH "$"
)
) AS a,
JSON_TABLE(
JSON_KEYS(@json_doc),
"$[*]"
COLUMNS(
seq FOR ordinality,
k CHAR(1) PATH "$"
)
) AS b
WHERE a.seq = b.seq;
这感觉很奇怪,因为它使用两个 JSON_TABLE
调用,对值和键进行交叉连接,然后保持对齐。我想找到一个像这样更简单的查询...
SELECT a.seq, b.k, a.v
FROM
JSON_TABLE(
@json_doc,
"$.*"
COLUMNS(
seq FOR ordinality,
k CHAR(1) PATH "?" -- <-- what do I put here to find each key?
v INT PATH "$"
)
) AS a,
我知道这个问题可能可以通过 CTE 或数字 table 和 JSON_EXTRACT
来解决。但是,如果可能的话,我想找到一些高性能和可读的东西。
您可以通过使用 ROW_NUMBER()
window 函数来使用枚举,同时通过使用 JSON_KEYS()
确定键值,然后使用 JSON_EXTRACT()
从中提取相应的键我们得到的数组如
WITH k AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY `jsdata` ORDER BY value DESC) AS rn,
JSON_KEYS(`jsdata`) AS jk
FROM `tab` AS t
JOIN JSON_TABLE(`jsdata`,'$.*' COLUMNS (value INT PATH '$')) j
)
SELECT JSON_UNQUOTE(JSON_EXTRACT(jk, CONCAT('$[',rn-1,']'))) AS "key",
value
FROM k
或使用以下查询更直接
SELECT JSON_UNQUOTE(
JSON_EXTRACT(JSON_KEYS(`jsdata`),
CONCAT('$[',
ROW_NUMBER() OVER(PARTITION BY `jsdata` ORDER BY value DESC)-1,
']'))
) AS "key", value
FROM `tab` AS t
JOIN JSON_TABLE(`jsdata`,'$.*' COLUMNS (value INT PATH '$')) j
尝试在获得 JSON_KEYS
作为行后直接执行 JSON_EXTRACT
:
WITH j AS (
SELECT CAST('{"a": 1, "b": "-1", "c": null}' AS JSON) o UNION ALL
SELECT CAST('{"x": 2, "y": "-2", "z": null}' AS JSON)
)
SELECT k, JSON_EXTRACT(j.o, CONCAT('$."', jt.k, '"')) v
FROM j
, JSON_TABLE(JSON_KEYS(o), '$[*]' COLUMNS (k VARCHAR(200) PATH '$')) jt;
Barbaros 的答案可以使用您提供的演示数据解决您的问题,但如果您的 json 对象在不同键下具有相同的值,则可能无法得到您想要的结果。