Extract feelds as key value from a json object in mariadb - mysql

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

Related

How to update json property of TimeStamp in oracle

I have one of the column audit_info with JSON data in the table:
{
"AddInfo":{
"UPN":"abc#abc.com",
"UserName":"abc#abc.com",
"TimeStamp":"2021-10-11T15:54:34:4805634Z",
"Source":"xyz"
},
"ChangeInfo":{
"UPN":"abc#abc.com",
"UserName":"abc#abc.com",
"TimeStamp":"2021-10-11T15:54:34:4832421Z",
"Source":"xyz"
}
}
I need to update TimeStamp of seconds decimal point from :4832421Z to .4832421Z
Can anyone please help me?
update the_table
set audit_info=??
Original Question:
If you do not have {:} anywhere else then you can use replace:
UPDATE table_name
SET audit_info = REPLACE(audit_info, '{:}', '{.}');
fiddle
Updated Question:
On later Oracle versions, if you want to update the last : to . in the paths $.AddInfo.TimeStamp and $.ChangeInfo.TimeStamp then you can use use JSON_TABLE to extract the timestamps and then simple string functions to extract the components before and after the last : and then use JSON_MERGEPATCH to update the specific paths:
MERGE INTO table_name dst
USING (
SELECT t.ROWID AS rid,
JSON_OBJECT(
KEY 'AddInfo' VALUE JSON_OBJECT(
KEY 'TimeStamp'
VALUE SUBSTR(addinfo_ts, 1, INSTR(addinfo_ts, ':', -1) - 1)
|| '.' || SUBSTR(addinfo_ts, INSTR(addinfo_ts, ':', -1) + 1)
),
KEY 'ChangeInfo' VALUE JSON_OBJECT(
KEY 'TimeStamp'
VALUE SUBSTR(changeinfo_ts, 1, INSTR(changeinfo_ts, ':', -1) - 1)
|| '.' || SUBSTR(changeinfo_ts, INSTR(changeinfo_ts, ':', -1) + 1)
)
) AS patch
FROM table_name t
CROSS APPLY JSON_TABLE(
t.audit_info,
'$'
COLUMNS
addinfo_ts VARCHAR2(30) PATH '$.AddInfo.TimeStamp',
changeinfo_ts VARCHAR2(30) PATH '$.ChangeInfo.TimeStamp'
) j
) src
ON (src.rid = dst.ROWID)
WHEN MATCHED THEN
UPDATE
SET audit_info = JSON_MERGEPATCH(audit_info, src.patch);
Then, for the sample data, after the MERGE the table contains:
AUDIT_INFO
{"AddInfo":{"UPN":"abc#abc.com","UserName":"abc#abc.com","TimeStamp":"2021-10-11T15:54:34.4805634Z","Source":"xyz"},"ChangeInfo":{"UPN":"abc#abc.com","UserName":"abc#abc.com","TimeStamp":"2021-10-11T15:54:34.4832421Z","Source":"xyz"}}
If you do not want to worry about specific paths then you can use a regular expression to match the timestamp:
UPDATE table_name
SET audit_info = REGEXP_REPLACE(
audit_info,
'("TimeStamp"\s*:\s*"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}):(\d+Z")',
'\1.\2'
);
fiddle
Try replace. This replace the json field an text and then you convert it again an json. It should be something like this:
update [name_of_the_table]
set audit_info = replace(audit_info::TEXT, ':', '.')::jsonb
where
....

Teradata SQL Split Single String into Table Rows

I have one string element, for example : "(1111, Tem1), (0000, Tem2)" and hope to generate a data table such as
var1
var2
1111
Tem1
0000
Tem2
This is my code, I created the lag token and filter with odd rows element.
with var_ as (
select '(1111, Tem1), (0000, Tem2)' as pattern_
)
select tbb1.*, tbb2.result_string as result_string_previous
from(
select tb1.*,
min(token) over(partition by 1 order by token asc rows between 1 preceding and 1 preceding) as min_token
from
table (
strtok_split_to_table(1, var_.pattern_, '(), ')
returns (outkey INTEGER, token INTEGER, result_string varchar(20))
) as tb1) tbb1
inner join (select min_token, result_string from tbb1) tbb2
on tbb1.token = tbb2.min_token
where (token mod 2) = 0;
But it seems that i can't generate new variables in "from" step and applied it directly in "join" step.
so I wanna ask is still possible to get the result what i want in my procedure? or is there any suggestion?
Thanks for all your assistance.
I wouldn't split / recombine the groups. Split each group to a row, then split the values within the row, e.g.
with var_ as (
select '(1111, Tem1), (0000, Tem2)' as pattern_
),
split1 as (
select trim(leading '(' from result_string) as string_
from
table ( /* split at & remove right parenthesis */
regexp_split_to_table(1, var_.pattern_, '\)((, )|$)','c')
returns (outkey INTEGER, token_nbr INTEGER, result_string varchar(256))
) as tb1
)
select *
from table(
csvld(split1.string_, ',', '"')
returns (var1 VARCHAR(16), var2 VARCHAR(16))
) as tb2
;

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 json array based on index in mysql

There are 2 queries. First is how to get the index of a json array
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(JSON_SEARCH(somecolumn1, 'one', 'macro', null,
'$.my_policies.inmy_policies.some_filters'),"[",-1),']',1)
as name FROM table1;
Second is to get nth element(below extracts 2nd element) from json array
select JSON_EXTRACT(somecolumn1,"$.my_policies.inmy_policies.some_filters[1]")
as name from table1;
I need to combine these 2 queries in a single command. First get the index and pass the index in JSON EXTRACT in nth position.
Try:
SELECT
JSON_EXTRACT(
#`json`,
CONCAT(
'$.my_policies.inmy_policies.some_filters[',
(
SELECT
SUBSTRING_INDEX(
SUBSTRING_INDEX(
JSON_SEARCH(
#`json`,
'one',
'macro',
null,
'$.my_policies.inmy_policies.some_filters[*].filter_name'
),
"[",
-1
),
']',
1
)
),
']'
)
) `json_filter`;
See dbfiddle.

How to Update a Specific Object in a JSON Array in Mysql 5.7

How can I update an object in an array based on a unique value in the object?
Let's say this is my json object stored in a table called objects and in a column called content
table: objects
id: 7383
content: { data:[{id: 111, active: 1 }, {id: 222, active: 1 }, {id: 333, active: 0 }] }
I can update objects if I know the position of the element in the array with
SET content = JSON_REPLACE(content,'$.data[1].active', 0)
Where id = 7383
However, if I don't know the position of the array, but I do know the value of id (for example 222) in the object, how can I update active to 0 for the object that has id: 222 ?
Currently, it's complicated to look up numerical values with MySQL JSON functions. In a JSON like the following, it would be simple:
{"id": "222", "active": 1}
There are many ways to get what you need, I present one that can give you ideas (modify everything that is necessary):
UPDATE `objects`
SET `objects`.`content` =
JSON_REPLACE(`objects`.`content`, CONCAT('$.data',
(SELECT
JSON_UNQUOTE(
REPLACE(
JSON_SEARCH(
REPLACE(
REPLACE(
REPLACE(
`der`.`content` ->> '$.data[*].id',
', ',
'","'),
']',
'"]'),
'[',
'["'),
'one',
'222'),
'$',
'')
)
FROM (SELECT `objects`.`content`
FROM `objects`
WHERE `objects`.`id` = 7383) `der`
), '.active'), 0)
WHERE `objects`.`id` = 7383;
Beware of possible performance problems.
See dbfiddle.
In the most recent version of MySQL (>= 8.0.4), the sentence would be much simpler:
UPDATE `objects`
INNER JOIN JSON_TABLE(
`objects`.`content`,
'$.data[*]' COLUMNS(
`rowid` FOR ORDINALITY,
`id` INT PATH '$.id'
)
) `der` ON `der`.`id` = 222
SET `objects`.`content` =
JSON_REPLACE(
`objects`.`content`,
CONCAT('$.data[', `der`.`rowid` - 1, '].active'),
0)
WHERE
`objects`.`id` = 7383;
See db-fiddle.
It can be achieved by combining the functions JSON_SEARCH, which returns a dirty json path to the item you need, and then, extract the value of the jsonpath with an array index, concatenate it with subpath we want to update and use JSON_SET to set a new value to the final json path (tested with MySQL 5.7.32):
-- INPUT ------------------------------------------------
-- unique value for an object in the array
SET #unique_value = "12345";
-- object field we want to update
SET #field_to_update = '.myField';
-- new value
SET #new_value = 1;
-- PROCESSING ------------------------------------------
-- Get json path to the item with specified #unique_value
-- RESULT: $.data[6].id
SET #temp_path = ( TRIM(BOTH '"' FROM ( SELECT JSON_SEARCH(json, 'one', #unique_value, NULL, "$.data")
FROM `my-table`
WHERE `column1` = "abcd" ) ));
-- We are looking for the bracket that delimits index within the array of documents: [11]
SET #closing_bracket_index = (SELECT LOCATE(']', #temp_path));
-- Get json path with index of an object for #unique_value
-- in MySQL, string indexing starts from position 1, not a zero
-- RESULT: $.data[6]
SET #item_path = ( SELECT SUBSTRING(#temp_path, 1, #closing_bracket_index) );
-- $.data[6].myFIeld
SET #item_path_to_update = ( SELECT CONCAT(#item_path, #field_to_update) );
-- UPDATE JSON STATEMENT
UPDATE `my-table`
SET json = JSON_SET(json-column, #item_path_to_update, #new_value)
WHERE `column1` = "abcd";

Categories