Is there an opposite of MySQL's JSON_ARRAY_APPEND? - mysql

I used UPDATE table SET col = JSON_ARRAY_APPEND(col, '$', 'BAZ') to add a value to my json column:
Before: ["FOO", "BAR"]
After: ["FOO", "BAR", "BAZ"]
How can I now remove the value, i.e. perform the reverse of JSON_ARRAY_APPEND? I've tried the following but it doesn't seem to pick up the value.
UPDATE table SET col = JSON_REMOVE(col, '$.BAZ')

You can remove array elements by position, not by value.
select json_remove('["FOO", "BAR", "BAZ"]', '$[2]') as array;
+----------------+
| array |
+----------------+
| ["FOO", "BAR"] |
+----------------+
But you can find the position with JSON_SEARCH():
select json_search('["FOO", "BAR", "BAZ"]', 'one', 'BAZ') as path;
+--------+
| path |
+--------+
| "$[2]" |
+--------+
You can see it strangely puts JSON double-quotes around that path. So you have to unquote it:
select json_unquote(json_search('["FOO", "BAR", "BAZ"]', 'one', 'BAZ')) as path;
+------+
| path |
+------+
| $[2] |
+------+
Then put it all together:
select json_remove('["FOO", "BAR", "BAZ"]', json_unquote(json_search('["FOO", "BAR", "BAZ"]', 'one', 'BAZ'))) as array;
+----------------+
| array |
+----------------+
| ["FOO", "BAR"] |
+----------------+
This would be a lot easier if you didn't use JSON arrays. Instead of using an array, put multi-valued attributes in a child table, with one value per row. Then you can delete using traditional SQL:
DELETE FROM child_table WHERE somvalue = 'BAZ';
I have answered a bunch of questions about using JSON in MySQL here on Stack Overflow, and I have yet to see an instance where using JSON is easier than using normalized tables.

Related

Minimize Mysql Json output

Mysql has a JSON_PRETTY() function to print human-readable JSON. I'm looking for the opposite functionality, to minimize JSON columns, getting rid of unnecessary whitespace, but haven't been able to find anything that does that. Is it possible to accomplish that with some combination of Mysql commands? I do need to do this in the SQL, not in application code.
INSERT INTO MY_TABLE(jsonPara) VALUES ('{"lamp": "hello world", "chair": "5"}');
select jsonPara, json_pretty(jsonPara) from MY_TABLE;
+-----------------------------------------+-------------------------------------------------+
|jsonPara |json_pretty(jsonPara) |
+-----------------------------------------+-------------------------------------------------+
|{"lamp": "hello world", "chair": "5"} |{ |
| |"lamp": "6", |
| |"chair": "5" |
| |} |
+-----------------------------------------+-------------------------------------------------+
I would like a result like {"lamp":"hello world","chair":"5"} (no spaces/new lines between keys and values and key/value pairs, array elements, etc.)
The best I can suggest is CAST(<expr> AS JSON). This reduces whitespace to a "normal" amount: one space after : and ,.
Here's your example document:
mysql> set #j = '{"lamp": "hello world", "chair": "5"}';
You know JSON_PRETTY() adds newlines and indentation:
mysql> select json_pretty(#j) as j;
+---------------------------------------------+
| j |
+---------------------------------------------+
| {
"lamp": "hello world",
"chair": "5"
} |
+---------------------------------------------+
Casting that expression back to JSON removes the extra whitespace:
mysql> select cast(json_pretty(#j) as json) as j;
+---------------------------------------+
| j |
+---------------------------------------+
| {"lamp": "hello world", "chair": "5"} |
+---------------------------------------+

Parsing JSon column value

I am trying to parse a json column from a table called Json_table which has 2 different types of elements,
keyfield | json_column | SomeotherField1 | SomeotherField2
----------------------------------------------------------------
keyfield1, | {"jField1":"Value1", ..."jField10":"Value10", "MapField":[{"Key": "key1", "Value":"Keyvalue1"}, {"Key": "key2", "Value":"Keyvalue2"}] | someothervalue | someothervalue
Using get_json_object function I can easily reach to jField1 to jField10 and MapField. But I dont know how i can parse MapField into further separate columns.
SELECT keyfield, get_json_object(json_column, '$.jField1') as jField1, get_json_object(json_column, '$.jField2') as jField2
FROM Json_table
I need to write query that would provide me results like this
Select Keyfield, jField1, jField2, .. , jField10, Key1, Key2 From Json_table
result as:
keyfield1 | Value1 | ... | Value10 | Keyvalue1 | Keyvalue2
I want to get all individual elements from within "Mapfield" part.
You can access MapField array by indexes, $.MapField[0].Value,
$.MapField[1].Value
with cte as (
select string('{"jField1":"Value1","jField10":"Value10", "MapField":[{"Key": "key1", "Value":"Keyvalue1"}, {"Key": "key2", "Value":"Keyvalue2"}]}')as jsn_col)
select get_json_object(jsn_col,'$.jField1')jField1,
get_json_object(jsn_col,'$.MapField[0].Value')key1,
get_json_object(jsn_col,'$.MapField[1].Value')key2 from cte;
+-------+---------+---------+
|jField1|key1 |key2 |
+-------+---------+---------+
|Value1 |Keyvalue1|Keyvalue2|
+-------+---------+---------+

mysql - avoid escaping double quotes in json functions

When I issue...
select JSON_REPLACE('{"tbl" : "cnf"}', '$', '{"tbl":"cnf4"}');
I get the following :
+--------------------------------------------------------+
| JSON_REPLACE('{"tbl" : "cnf"}', '$', '{"tbl":"cnf4"}') |
+--------------------------------------------------------+
| "{\"tbl\":\"cnf4\"}" |
+--------------------------------------------------------+
And it gets stored in my database the same say, with backslashes. I want to have no backslashes in my database. How can I achieve that?
I expect a reponse like:
{"tbl":"cnf4"}
Wrap in JSON_UNQUOTE
select JSON_UNQUOTE(JSON_REPLACE('{"tbl" : "cnf"}', '$', '{"tbl":"cnf4"}'));
+----------------------------------------------------------------------+
| JSON_UNQUOTE(JSON_REPLACE('{"tbl" : "cnf"}', '$', '{"tbl":"cnf4"}')) |
+----------------------------------------------------------------------+
| {"tbl":"cnf4"} |
+----------------------------------------------------------------------+
1 row in set (0.0005 sec)
This helped me to unescape when I pushed new object to an existing array
json_array_append(data, '$', cast(? as json))

Unquote a value while constructing JSON Object in MySql query

MySql version 5.7.12
Query
SELECT JSON_OBJECT(unix_timestamp(created_time),JSON_OBJECT(status,cast(concat(count(status))as char))) FROM table_name where status='NEW' and unix_timestamp(created_time) between 1512543400 and 1532716199 GROUP BY unix_timestamp(created_time);
Result
+--------------------------------------------------------------------------------------------------+
| JSON_OBJECT(unix_timestamp(created_time),JSON_OBJECT(status,cast(concat(count(status))as char))) |
+--------------------------------------------------------------------------------------------------+
| {"1526447587": {"NEW": "1"}} |
| {"1530170666": {"NEW": "1"}} |
+--------------------------------------------------------------------------------------------------+
I want to unquote the value "1" like following, how to do this ??
{"1526447587": {"NEW": 1}}
Just remove the cast to char:
SELECT JSON_OBJECT(UNIX_TIMESTAMP(NOW()), JSON_OBJECT('hello', 312)) AS output
FROM dual
This outputs:
{"1532691464": {"hello": 312}}
Demo

How to search JSON array in MySQL?

Let's say I have a JSON column named data in some MySQL table, and this column is a single array. So, for example, data may contain:
[1,2,3,4,5]
Now I want to select all rows which have a data column where one of its array elements is greater than 2. Is this possible?
I tried the following, but seems it is always true regardless of the values in the array:
SELECT * from my_table
WHERE JSON_EXTRACT(data, '$[*]') > 2;
You may search an array of integers as follows:
JSON_CONTAINS('[1,2,3,4,5]','7','$') Returns: 0
JSON_CONTAINS('[1,2,3,4,5]','1','$') Returns: 1
You may search an array of strings as follows:
JSON_CONTAINS('["a","2","c","4","x"]','"x"','$') Returns: 1
JSON_CONTAINS('["1","2","3","4","5"]','"7"','$') Returns: 0
Note: JSON_CONTAINS returns either 1 or 0
In your case you may search using a query like so:
SELECT * from my_table
WHERE JSON_CONTAINS(data, '2', '$');
SELECT JSON_SEARCH('["1","2","3","4","5"]', 'one', "2") is not null
is true
SELECT JSON_SEARCH('["1","2","3","4","5"]', 'one', "6") is not null
is false
Since MySQL 8 there is a new function called JSON_TABLE.
CREATE TABLE my_table (id INT, data JSON);
INSERT INTO my_table VALUES
(1, "[1,2,3,4,5]"),
(2, "[0,1,2]"),
(3, "[3,4,-10]"),
(4, "[-1,-2,0]");
SELECT DISTINCT my_table.*
FROM my_table, JSON_TABLE(data, "$[*]" COLUMNS(nr INT PATH '$')) as ids
WHERE ids.nr > 2;
+------+-----------------+
| id | data |
+------+-----------------+
| 1 | [1, 2, 3, 4, 5] |
| 3 | [3, 4, -10] |
+------+-----------------+
2 rows in set (0.00 sec)
I use a combination of JSON_EXTRACT and JSON_CONTAINS (MariaDB):
SELECT * FROM table WHERE JSON_CONTAINS(JSON_EXTRACT(json_field, '$[*].id'), 11, '$');
I don't know if we found the solution.
I found with MariaDB a way, to search path in a array. For example, in array [{"id":1}, {"id":2}], I want find path with id equal to 2.
SELECT JSON_SEARCH('name_field', 'one', 2, null, '$[*].id')
FROM name_table
The result is:
"$[1].id"
The asterisk indicate searching the entire array
This example works for me with mysql 5.7 above
SET #j = '{"a": [ "8428341ffffffff", "8428343ffffffff", "8428345ffffffff", "8428347ffffffff","8428349ffffffff", "842834bffffffff", "842834dffffffff"], "b": 2, "c": {"d": 4}}';
select JSON_CONTAINS(JSON_EXTRACT(#j , '$.a'),'"8428341ffffffff"','$') => returns 1
notice about " around search keyword, '"8428341ffffffff"'
A possible way is to deal with the problem as string matching. Convert the JSON to string and match.
Or you can use JSON_CONTAINS.
You can use JSON extract to search and select data
SELECT data, data->"$.id" as selectdata
FROM table
WHERE JSON_EXTRACT(data, "$.id") = '123'
#ORDER BY c->"$.name";
limit 10 ;
SET #doc = '[{"SongLabels": [{"SongLabelId": "111", "SongLabelName": "Funk"}, {"SongLabelId": "222", "SongLabelName": "RnB"}], "SongLabelCategoryId": "test11", "SongLabelCategoryName": "曲风"}]';
SELECT *, JSON_SEARCH(#doc, 'one', '%un%', null, '$[*].SongLabels[*].SongLabelName')FROM t_music_song_label_relation;
result: "$[0].SongLabels[0].SongLabelName"
SELECT song_label_content->'$[*].SongLabels[*].SongLabelName' FROM t_music_song_label_relation;
result: ["Funk", "RnB"]
I have similar problem, search via function
create function searchGT(threshold int, d JSON)
returns int
begin
set #i = 0;
while #i < json_length(d) do
if json_extract(d, CONCAT('$[', #i, ']')) > threshold then
return json_extract(d, CONCAT('$[', #i, ']'));
end if;
set #i = #i + 1;
end while;
return null;
end;
select searchGT(3, CAST('[1,10,20]' AS JSON));
This seems to be possible with to JSON_TABLE function. It's available in mysql version 8.0 or mariadb version 10.6.
With this test setup
CREATE TEMPORARY TABLE mytable
WITH data(a,json) AS (VALUES ('a','[1]'),
('b','[1,2]'),
('c','[1,2,3]'),
('d','[1,2,3,4]'))
SELECT * from data;
we get the following table
+---+-----------+
| a | json |
+---+-----------+
| a | [1] |
| b | [1,2] |
| c | [1,2,3] |
| d | [1,2,3,4] |
+---+-----------+
It's possible to select every row from mytable wich has a value greater than 2 in the json array with this query.
SELECT * FROM mytable
WHERE TRUE IN (SELECT val > 2
FROM JSON_TABLE(json,'$[*]'
columns (val INT(1) path '$')
) as json
)
Returns:
+---+-----------+
| a | json |
+---+-----------+
| c | [1,2,3] |
| d | [1,2,3,4] |
+---+-----------+