Related
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
I have the following field (which is a string) of one of my tables:
'[{"id":"6","value":["http://www.google.com","http://www.google.com","new"]},
{"id":"7","value":"Reuters"},
{"id":"20","value":"2017/03/17"}]'`
As you can see, the last part, the one with id=20 has a date on it, 2017/03/17.
Can someone tell me how can I update that field so I can get the output 2017-03-17?
My desired output after the update would be:
'[{"id":"6","value":["http://www.google.com","http://www.google.com","new"]},
{"id":"7","value":"Reuters"},
{"id":"20","value":"2017-03-17"}]'`
If I use:
UPDATE mytable
SET myfield = replace(myfield , '/', '-');
It removes the / of the links, and I want to mantain them.
PS: The ids of the fields are always the same (the one for the date is always 20 for example) the only thing that changes are the values.
you can do it with a query like this:
UPDATE mytable
SET myfield = CONCAT (
SUBSTRING_INDEX(myfield, '"id":"20","value":', 1)
, '"id":"20","value":',
, REPLACE (SUBSTRING_INDEX(myfield, '"id":"20","value":', -1),'/','-')
)
WHERE ....:
sample
mysql> SELECT
-> CONCAT (
-> SUBSTRING_INDEX('[{"id":"6","value":["http://www.google.com","http://www.google.com","new"]},
'> {"id":"7","value":"Reuters"},
'> {"id":"20","value":"2017/03/17"}]', '"id":"20","value":', 1),
-> '"id":"20","value":',
-> REPLACE (SUBSTRING_INDEX('[{"id":"6","value":["http://www.google.com","http://www.google.com","new"]},
'> {"id":"7","value":"Reuters"},
'> {"id":"20","value":"2017/03/17"}]', '"id":"20","value":', -1),'/','-')
-> ) AS result;
+------------------------------------------------------------------------------------------------------------------------------------------------+
| result |
+------------------------------------------------------------------------------------------------------------------------------------------------+
| [{"id":"6","value":["http://www.google.com","http://www.google.com","new"]},
{"id":"7","value":"Reuters"},
{"id":"20","value":"2017-03-17"}] |
+------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0,00 sec)
mysql>
Maybe there is a better way, But you can :
split the string into rows using { as separator
find the row with "ID=20"
do the replace
join all rows back together using GROUP_CONCAT
DEMO
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(T.json, '{', numbers.n), '{', -1) name,
CASE WHEN INSTR(SUBSTRING_INDEX(SUBSTRING_INDEX(T.json, '{', numbers.n), '{', -1), '"id":"20"') > 0
THEN REPLACE(SUBSTRING_INDEX(SUBSTRING_INDEX(T.json, '{', numbers.n), '{', -1), '/', '-')
ELSE SUBSTRING_INDEX(SUBSTRING_INDEX(T.json, '{', numbers.n), '{', -1)
END as result
FROM
(select 1 n union all
select 2 union all select 3 union all
select 4 union all select 5) numbers
JOIN (
SELECT '[{"id":"6","value":["http://www.google.com","http://www.google.com","new"]},
{"id":"7","value":"Reuters"},
{"id":"20","value":"2017/03/17"}]' as json
) T
on CHAR_LENGTH(T.json)
-CHAR_LENGTH(REPLACE(T.json, '{', ''))>=numbers.n-1
order by
n
OUTPUT
OPTION 2
SELECT SUBSTRING(json, 1, pos_begin - 1 ) as side_left,
REPLACE(SUBSTRING(json, pos_begin, pos_end - pos_begin + 1), '/', '-') as side_middle,
SUBSTRING(json, pos_end + 1, LENGTH(json)) as side_end
FROM (
SELECT LOCATE('{"id":"20"', json) as pos_begin,
LOCATE('}', json, LOCATE('{"id":"20"', json)) as pos_end,
T.json
FROM (
SELECT '[{"id":"6","value":["http://www.google.com","http://www.google.com","new"]},
{"id":"7","value":"Reuters"},
{"id":"20","value":"2017/03/17"}]' as json
) T
) S
;
OUTPUT
I have a column which returns
a:2:{i:0;s:10:"Properties";i:1;s:14:"Movable Assets";}
I would like to return only:
Properties, Movable Assets
How can I use a select statement to retrieve the values between the " symbols
these are serialize values, you can use php, to get your desired results.
you can use , unserialize which will return an array then you can use implode to get the comma separated values.
example
Use SUBSTRING_INDEX().
SUBSTRING_INDEX() takes a string argument followed by a delimiter character and the number of parts to return. After you break up the string using the delimiter, that number of parts is returned as a single string.
select concat(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
'a:2:{i:0;s:10:"Properties";i:1;s:14:"Movable Assets";}',
'"',
4
),
'"',
2
),
'"',
-1
),
",",
SUBSTRING_INDEX(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
'a:2:{i:0;s:10:"Properties";i:1;s:14:"Movable Assets";}',
'"',
4
),
'"',
4
),
'"',
-1
)
);
Use combination of LOCATE() and SUBSTRING().
Definitions: https://dev.mysql.com/doc/refman/5.0/en/string-functions.html
Or better -- migrate the data to actually be retrievable.
I have a column that has comma separated data:
1,2,3
3,2,1
4,5,6
5,5,5
I'm trying to run a search that would query each value of the CSV string individually.
0<first<5 and 1<second<3 and 2<third<4
I get that I could return all queries and split it myself and compare it myself. I'm curious if there is a way to do this so MySQL does that processing work.
Thanks!
Use
substring_index(`column`,',',1) ==> first value
substring_index(substring_index(`column`,',',-2),',',1)=> second value
substring_index(substring_index(`column`,',',-1),',',1)=> third value
in your where clause.
SELECT * FROM `table`
WHERE
substring_index(`column`,',',1)<0
AND
substring_index(`column`,',',1)>5
It seems to work:
substring_index ( substring_index ( context,',',1 ), ',', -1)
substring_index ( substring_index ( context,',',2 ), ',', -1)
substring_index ( substring_index ( context,',',3 ), ',', -1)
substring_index ( substring_index ( context,',',4 ), ',', -1)
it means 1st value, 2nd, 3rd, etc.
Explanation:
The inner substring_index returns the first n values that are comma separated. So if your original string is "34,7,23,89", substring_index( context,',', 3) returns "34,7,23".
The outer substring_index takes the value returned by the inner substring_index and the -1 allows you to take the last value. So you get "23" from the "34,7,23".
Instead of -1 if you specify -2, you'll get "7,23", because it took the last two values.
Example:
select * from MyTable where substring_index(substring_index(prices,',',1),',',-1)=3382;
Here, prices is the name of a column in MyTable.
Usually substring_index does what you want:
mysql> select substring_index("foo#gmail.com","#",-1);
+-----------------------------------------+
| substring_index("foo#gmail.com","#",-1) |
+-----------------------------------------+
| gmail.com |
+-----------------------------------------+
1 row in set (0.00 sec)
You may get what you want by using the MySQL REGEXP or LIKE.
See the MySQL Docs on Pattern Matching
As an addendum to this, I've strings of the form:
Some words 303
where I'd like to split off the numerical part from the tail of the string.
This seems to point to a possible solution:
http://lists.mysql.com/mysql/222421
The problem however, is that you only get the answer "yes, it matches", and not the start index of the regexp match.
Here is another variant I posted on related question. The REGEX check to see if you are out of bounds is useful, so for a table column you would put it in the where clause.
SET #Array = 'one,two,three,four';
SET #ArrayIndex = 2;
SELECT CASE
WHEN #Array REGEXP CONCAT('((,).*){',#ArrayIndex,'}')
THEN SUBSTRING_INDEX(SUBSTRING_INDEX(#Array,',',#ArrayIndex+1),',',-1)
ELSE NULL
END AS Result;
SUBSTRING_INDEX(string, delim, n) returns the first n
SUBSTRING_INDEX(string, delim, -1) returns the last only
REGEXP '((delim).*){n}' checks if there are n delimiters (i.e. you are in bounds)
Building on #Oleksiy's answer, here is one that can work with strings of variable segment lengths (within reasonable limits), for example comma-separated addresses:
SELECT substring_index ( substring_index ( address,',',1 ), ',', -1) AS address_line_1,
IF(address_parts > 1, substring_index ( substring_index ( address,',',2 ), ',', -1), '') AS address_line_2,
IF(address_parts > 2, substring_index ( substring_index ( address,',',3 ), ',', -1), '') AS address_line_3,
IF(address_parts > 3, substring_index ( substring_index ( address,',',4 ), ',', -1), '') AS address_line_4,
IF(address_parts > 4, substring_index ( substring_index ( address,',',5 ), ',', -1), '') AS address_line_5
FROM (
SELECT address, LENGTH(address) - LENGTH(REPLACE(address, ',', '')) AS address_parts
FROM mytable
) AS addresses
It's working..
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(
SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(col,'1', 1), '2', 1), '3', 1), '4', 1), '5', 1), '6', 1)
, '7', 1), '8', 1), '9', 1), '0', 1) as new_col
FROM table_name group by new_col;