Using JSON_TABLE to convert an ARRAY into Columns - mysql

I have a database column (named "details") formatted as a JSON object that contains the following data:
'{"300-000012": {"is_complete": "False", "is_in_progress": "True"},
"300-000018": {"is_complete": "True", "is_in_progress": "False"}}'
I can't seem to convert the Array into Columns. I've tried
SELECT mh.*, jt.*
FROM history AS mh,
JSON_TABLE (mh.details, '$[*]'
COLUMNS (
NESTED PATH '$.*' COLUMNS (jt_complete VARCHAR(255) PATH '$.is_complete'),
NESTED PATH '$.*' COLUMNS (jt_progress VARCHAR(255) PATH '$.is_in_progress')
)
) AS jt)
But I get an Error Code
Error Code: 3143. Invalid JSON path expression
Ideally I would get something like:
details jt_complete jt_progress
300-000012 FALSE TRUE
300-000018 TRUE FALSE
Any help would be appreciated. Thx

This is a tricky one because the keys of the object are variable. This means you need to extract the keys and the values separately for each object. The values can be connected by using an ordinality column for each JSON_TABLE and joining them on that:
SELECT mh.id, jk.details, jt.jt_complete, jt.jt_progress
FROM history mh
JOIN JSON_TABLE(
JSON_KEYS(mh.details),
'$[*]' COLUMNS (
rn FOR ORDINALITY,
details VARCHAR(10) PATH '$'
)
) jk
JOIN JSON_TABLE(
JSON_EXTRACT(mh.details, '$.*'),
'$[*]' COLUMNS (
rn FOR ORDINALITY,
jt_complete VARCHAR(10) PATH '$.is_complete',
jt_progress VARCHAR(10) PATH '$.is_in_progress'
)
) jt ON jt.rn = jk.rn
Demo on dbfiddle

Related

How to get data from json in a mysql query correctly?

I'm trying to pull data from json and insert it into a table. New rows appear in the table by number as in json, but all fields have an empty value, NULL. Please help me figure it out.
[{"name":"ivan","city":"london","kurs":"1"},{"name":"lena","city":"tokio","kurs":"5"},{"name":"misha","city":"kazan","kurs":"3"}]
SET #json = CONVERT(LOAD_FILE('/var/lib/mysql-files/myfile.json') using utf8mb4);
REPLACE INTO test (name, city, kurs)
SELECT
JSON_VALUE(#json, '$.name') as name,
JSON_VALUE(#json, '$.city') as city,
JSON_VALUE(#json, '$.kurs') as kurs
FROM JSON_TABLE(#json,'$[*]' COLUMNS (data JSON PATH '$')) jsontable
Use this, (and change VARCAR(20) to the appropriate definition):
set #json = '[{"name":"ivan","city":"london","kurs":"1"},{"name":"lena","city":"tokio","kurs":"5"},{"name":"misha","city":"kazan","kurs":"3"}]';
select * from json_table(#json,'$[*]' columns(name varchar(20) path '$.name',
city varchar(20) path '$.city',
kurs varchar(20) path '$.kurs')) as jsontable;
see: DBFIDDLE
output:
name
city
kurs
ivan
london
1
lena
tokio
5
misha
kazan
3

Sum fields inside json array in mysql

I have this table:
CREATE TABLE stackoverflow_question (
id int NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
json_ob mediumtext default null,
PRIMARY KEY (id)
);
I do some inserts:
insert into stackoverflow_question values(null, 'albert', '[{name: "albert1", qt: 2},{name: "albert2", qt: 2}]');
insert into stackoverflow_question values(null, 'barbara', '[{name: "barbara1", qt: 4},{name: "barbara2", qt: 7}]');
insert into stackoverflow_question values(null, 'paul', '[{name: "paul1", qt: 9},{name: "paul2", qt: 11}]');
Eventually, I will need to sort this table by total quantity.
in the examples above, "paul" has quantity = 20, while "barbara" has quantity = 11. And "albert" has quantity = 4.
Is it possible to create a select statement where a new field is created on the fly? Something like this:
SELECT
SUM (loop json_ob and sum all the quantity fields) AS total_quantity,
id,
name
FROM
stackoverflow_question
ORDER BY total_quantity
If json_ob is actually a valid json object then you can use JSON_TABLE() to extract the quantities and aggregate:
SELECT s.*, SUM(t.qt) total_quantity
FROM stackoverflow_question s,
JSON_TABLE(json_ob, '$[*]' COLUMNS (qt INTEGER PATH '$.qt')) t
GROUP BY s.id
ORDER BY total_quantity DESC;
See the demo.
According to jsonlint your JSON is not valid.
That's why this SQL returns an error (ERROR 3141 (22032): Invalid JSON text in argument 1 to function json_table: "Missing a name for object member." at position 2.")
SELECT
j.name, j.qt
FROM JSON_TABLE('[{name: "paul1", qt: 9},{name: "paul2", qt: 11}]',
"$[*]" COLUMNS (name varchar(20) PATH "$.name", qt int PATH "$.qt")) j ;
and this will return the values:
SELECT
j.name, j.qt
FROM JSON_TABLE('[{"name": "paul1", "qt": 9},{"name": "paul2", "qt": 11}]',
"$[*]" COLUMNS (name varchar(20) PATH "$.name", qt int PATH "$.qt")) j ;
output:
name
qt
paul1
9
paul2
11
You can convert your relaxedJSON, to JSON, using tools like : www.relaxedjson.org

Add a property to every object in a json array in sql sever

I need to add "Description" property with "" value to all items in my json array.
I have tried :
JSON_MODIFY(ReasonCodes, '$[0].Description', '')
and getting result as:
[
{"Name":"jhfghgh","Code":"89798","Note":"dfgbcbxcbx","Description":""},
{"Name":"test7889","Code":"9787","Note":""}
]
basically i want that properties should be also in 2nd or any number of array as well of that json object.
The function JSON_MODIFY() doesn't support wild cards for value of the path parameter, so if the input JSON has a variable structure, you may try to parse the ReasonCodes JSON array with OPENJSON() and default schema, modify each item and aggregate the rows to build the final ouptut:
Table:
CREATE TABLE PD (ReasonCodes varchar(1000))
INSERT INTO PD (ReasonCodes)
VALUES ('[
{"Name":"test1","Code":"0001","Note":"dfgbcbxcbx","Description":null},
{"Name":"test2","Code":"0002","Note":"dfgbcbxcbx","Description":"ABCD"},
{"Name":"test3","Code":"0003","Note":""}
]')
Statement:
UPDATE PD
SET ReasonCodes = CONCAT(
'[',
(
SELECT STRING_AGG(JSON_MODIFY([value], '$.Description', ''), ',')
FROM OPENJSON(ReasonCodes)
),
']'
)
If you need to change the $.Description key, but only when the keys exists, you need a different statement:
UPDATE PD
SET ReasonCodes = CONCAT(
'[',
(
SELECT STRING_AGG(
CASE
WHEN j2.DescriptionCount > 0 THEN JSON_MODIFY(j1.[value], '$.Description', '')
ELSE JSON_QUERY(j1.[value])
END,
','
)
FROM OPENJSON(ReasonCodes) j1
OUTER APPLY (
SELECT COUNT(*)
FROM OPENJSON(j1.[value])
WHERE [key] = 'Description'
) j2 (DescriptionCount)
),
']'
)

Extract feelds as key value from a json object in mariadb

Hello I want to extract the different field values of a json object as key value pairs, but I'm not able to do that.
I tried this
SELECT JSON_EXTRACT(chapters, '$[*].Id', '$[*].Name') AS rec
FROM `Novels`
WHERE 1
but it result looks like this
["1","first Name","2","second name"]
any idea on how to convert it to something like this
{"1":"first Name","2":"second name"}
Thanks in advance!
Depending on the result, the concerned value of the chapters column should be
'[ {"Id":"1","Name":"first name"}, {"Id":"2","Name":"second name"} ]'
JSON_EXTRACT() can be applied for each element of the array in order to determine Id values as keys part, and Name values as values part.
And then, JSON_UNQUOTE() can be applied to get rid of double-quotes while generating rows for each individual array elements. JSON_OBJECTAGG is used to aggregate all those extracted objects at the last step provided that MariaDB version is 10.5+:
WITH n AS
(
SELECT #i := #i + 1 AS rn,
JSON_UNQUOTE(JSON_EXTRACT(chapters, CONCAT('$[',#i-1,'].Id'))) AS js_id,
JSON_UNQUOTE(JSON_EXTRACT(chapters, CONCAT('$[',#i-1,'].Name'))) AS js_name
FROM information_schema.tables
CROSS JOIN ( SELECT #i := 0, chapters FROM `Novels` ) n
WHERE #i < JSON_LENGTH(JSON_EXTRACT(chapters, '$[*]'))
)
SELECT JSON_OBJECTAGG(js_id,js_name) AS Result
FROM n
A Workaround might be given for DB version prior to 10.5 as
SELECT CONCAT('{',
GROUP_CONCAT(
REPLACE(
REPLACE( JSON_OBJECT(js_id,js_name) , '}', '')
, '{', '')
)
, '}') AS Result
FROM n
Demo
One option uses json_table() to unnest the array to rows (available in MySQL 8 only) then aggregation:
select
t.*,
(
select json_objectagg('id', x.id, 'name', x.name)
from json_table(
t.chapter,
'$[*]'
columns (
id int path '$.Id',
name varchar(50) path '$.Name'
)
) as x
) as obj
from mytable t

How can I update specific value from MySQL JSON column

I'm trying to update a specific value in JSON array from a MySQL column by searching for specific ID, I want it to be dynamic without specifying array key.
Here is what I tried:
update myTable JSON_REPLACE(`data`, $.OD, '3/1')
where JSON_EXTRACT(`data`, '$.ID') = 'some id';
Here is my column data:
[
{"ID":63010092,"IT":"OV80419947-63010092_1_3","OD":"21/10","SU":0,"OR":0},
{"ID":63010093,"IT":"OV80419947-63010093_1_3","OD":"8/5","SU":0,"OR":1},
{"ID":63010094,"IT":"OV80419947-63010094_1_3","OD":"8/5","SU":0,"OR":2}
]
Try (adjust everything you consider necessary):
UPDATE `myTable`
INNER JOIN JSON_TABLE(
`myTable`.`data`,
'$[*]'
COLUMNS(
`position` FOR ORDINALITY,
`ID` INT PATH '$.ID'
)
) `der`
SET `data` = JSON_REPLACE(
`myTable`.`data`,
CONCAT('$[', `der`.`position` - 1, '].OD'),
'3/1'
)
WHERE `der`.`ID` = 63010092;
See dbfiddle.