简体   繁体   中英

Expand JSON with unknown keys to rows with MySQL JSON_TABLE

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

Demo

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM