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
....
Related
I am using JSON_ARRAY_APPEND to update an array object in an existing entry in the table.
I am doing it via code and query looks as below:
UPDATE table t1
SET t1.value = JSON_ARRAY_APPEND('[]', '$', JSON_OBJECT('desc', '${desc}',
'hosts', '[${hosts}]'))
WHERE t1.key = '${key1}
My resulting value looks as below:
[{"desc": "this is desc", "hosts": "[host1,host2, host3]"}]
Desired Output for value is:
[{"desc": "this is desc", "hosts": ["host1","host2","host3"]"}]
You don't need to use JSON_ARRAY_APPEND() since you are creating a new array.
Use JSON_ARRAY(). This ensures the array will be formatted as valid JSON. You should try to avoid writing code that forms JSON by concatenating commas and brackets and quotes, because it's difficult to ensure this will always produce valid JSON format. It's fragile code.
Also, use query parameters instead of trying to interpolate code variables into your SQL statement.
UPDATE table t1
SET t1.value = JSON_ARRAY(
JSON_OBJECT(
'desc', ?,
'hosts', JSON_ARRAY(?, ?, ?, ?))
WHERE t1.key = ?
You'll need one ? parameter marker for each host you want in your hosts array.
``
SELECT CONCAT('"', REPLACE('${hosts}', ',', '","'), '"');
UPDATE table t1
SET t1.value = JSON_ARRAY_APPEND('[]', '$', JSON_OBJECT('desc', '${desc}', 'hosts', '[${hosts}]'))
WHERE t1.key = '${key1};
SET #host = 'host1,host2, host3';
UPDATE table t1
SET t1.value = JSON_ARRAY(
JSON_OBJECT(
'desc', 'this is desc',
'hosts',
JSON_ARRAY(
SUBSTRING_INDEX(#host, ',', 1),
SUBSTRING_INDEX(SUBSTRING_INDEX(#host, ',', 2), ',', -1) ,
SUBSTRING_INDEX(SUBSTRING_INDEX(#host, ',', 3), ',', -1)))) ;
''
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)
),
']'
)
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 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";
I'm don't have a lot of knowledge of MySql (or SQL in general) so sorry for the noobness.
I'm trying to update a bunch of String entries this way:
Lets say we have this:
commands.firm.pm.Stuff
Well I want to convert that into:
commands.firm.pm.print.Stuff
Meaning, Add the .print after pm, before "Stuff" (where Stuff can be any Alphanumerical String).
How would I do this with a MySql Query? I'm sure REGEXP has to be used, but I'm not sure how to go about it.
Thanks
Try something like this. It finds the last period and inserts your string there:
select insert(s, length(s) - instr(reverse(s), '.') + 1, 0, '.print')
from (
select 'commands.firm.pm.Stuff' as s
) a
To update:
update MyTable
set MyColumn = insert(MyColumn, length(MyColumn) - instr(reverse(MyColumn), '.') + 1, 0, '.print')
where MyColumn like 'commands.firm.pm.%'
Perhaps use a str_replace to replace commands.firm.pm to commands.firm.pm.print
$original_str = "commands.firm.pm.15hhkl15k0fak1";
str_replace("commands.firm.pm", "commands.firm.pm.print", $original_str);
should output: commands.firm.pm.print.15hhkl15k0fak1
then update your table with the new value...How to do it all in one query (get column value and do the update), I do not know. All I can think of is you getting the column value in one query, doing the replacement above, and then updating the column with the new value in a second query.
To update rows that end in '.Stuff' only:
UPDATE TableX
SET Column = CONCAT( LEFT( CHAR_LENGTH(Column) - CHAR_LENGTH('.Stuff') )
, '.print'
, '.Stuff'
)
WHERE Column LIKE '%.Stuff'
To update all rows - by appending .print just before the last dot .:
UPDATE TableX
SET Column = CONCAT( LEFT( CHAR_LENGTH(Column)
- CHAR_LENGTH(SUBSTRING_INDEX(Column, '.', -1))
)
, 'print.'
, SUBSTRING_INDEX(Column, '.', -1)
)
WHERE Column LIKE '%.%'