I have a MySQL 8.0.22 JSON column containing objects with keys that aren't known in advance:
'{"x": 1, "y": 2, "z": 3}'
'{"e": 4, "k": 5}'
I want to use JSON_TABLE
to expand these values into multiple rows containing key value pairs:
key | value |
---|---|
x | 1 |
y | 2 |
z | 3 |
e | 4 |
k | 5 |
The difficulty of course is that the keys aren't known a priori . The best thing I've come up with is...
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;
This feels strange because it uses two JSON_TABLE
calls, does a cross join on the values and keys, then keeps the ones that align. I'd like to find a simpler query like this...
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,
I know this problem can probably be solved with CTEs or a numbers table and JSON_EXTRACT
. But, I'd like to find something performant and readable if possible.
You can use enumarete by using ROW_NUMBER()
window function while determining the key values through use of JSON_KEYS()
, and then extract the respective keys by using JSON_EXTRACT()
from the arrays we got such as
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
or use the following query as being more straightforward
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
Try to do JSON_EXTRACT
directly after you got the JSON_KEYS
as rows:
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;
The answer by Barbaros can solve your problem with the demo data you provided, but it may not get what you want if your json objects have same value under different keys.
Try to do JSON_EXTRACT
directly after you got the JSON_KEYS
as rows:
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;
The answer by Barbaros can solve your problem with the demo data you provided, but it may not get what you want if your json objects have same value under different keys.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.