Having this json:
{
f1: "abc",
f2: [
{id: 1, val:"a"},
{id: 2, val:"b"},
{id: 3, val:"c"}
],
f3: [
"a",
"b",
"c"
]
}
Update:
As an example:
SELECT JSON_SEARCH(
'{"f1": "abc", "f2": [{"id": "1", "val":"a"},{"id": "2", "2":"b"},{"id": 3, "val":"2"}], "f3": ["a","b","c"]}',
'all', '1', null, '$.f2[*].id');
returns the needed path for f2->id==1
I can then use
select json_set(
'{"f1": "abc", "f2": [{"id": "1", "val":"a"},{"id": "2", "2":"b"},{"id": 3, "val":"2"}], "f3": ["a","b","c"]}',
'$.f2[0].val', 'd');
to update the data.
but
SELECT JSON_SEARCH(
'{"f1": "abc", "f2": [{"id": 1, "val":"a"},{"id": "2", "2":"b"},{"id": 3, "val":"2"}], "f3": ["a","b","c"]}',
'all', '1', null, '$.f2[*].id');
won't find f2->id==1 . Neither does
SELECT JSON_SEARCH(
'{"f1": "abc", "f2": [{"id": 1, "val":"a"},{"id": "2", "2":"b"},{"id": 3, "val":"2"}], "f3": ["a","b","c"]}',
'all', 1, null, '$.f2[*].id');
=========
update 2:
I will just save the IDs as string... But right now i have onother problem:
SELECT JSON_SEARCH(
'{"mm": [{"id":"1","field":"test","value":33}]}',
'one', '1', null, '$.mm[*].id') as path;
is working
SELECT * FROM document_data where document_id=5;
update document_data set data=JSON_SET(data, '$.mm', json_array()) where document_id=5;
update document_data set data=JSON_ARRAY_APPEND(data, '$.mm', '{"id":"1","field":"test","value":33}') where document_id=5;
SELECT JSON_SEARCH(data, 'one', '1', null, '$.mm[*].id') as path from document_data where id='5';
is not working. Seems to be the quoting.
Can someone help?
==========
how can i update f2 where id==2 using JSON_SET?
Tried everything but i can't seem to figure out how to do it.
Many thanks
Rene
JSON_SEARCH, by design, seems to search only strings, see WL#7909: Server side JSON functions :: JSON_SEARCH.
One option, very unintuitive is to use something like (be careful with performance problems):
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.20 |
+-----------+
1 row in set (0.00 sec)
mysql> SET #`json` := '{
'> "f1": "abc",
'> "f2": [
'> {"id": 1, "val": "a"},
'> {"id": 2, "val": "b"},
'> {"id": 3, "val": "c"}
'> ],
'> "f3": ["a", "b", "c"]
'> }',
-> #`value` := 2,
-> #`base_path` := '$.f2';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT JSON_SEARCH(
-> REPLACE(
-> REPLACE(
-> REPLACE(
-> JSON_EXTRACT(#`json`, CONCAT(#`base_path`, '[*].id')),
-> ', ', '","'),
-> '[', '["'),
-> ']', '"]'),
-> 'one', #`value`) INTO #`path`;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT #`path`;
+---------+
| #`path` |
+---------+
| "$[1]" |
+---------+
1 row in set (0.00 sec)
mysql> SELECT
-> CONCAT(
-> REPLACE(
-> JSON_UNQUOTE(#`path`),
-> '$',
-> #`base_path`
-> ),
-> '.val') INTO #`path`;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT #`path`;
+-------------+
| #`path` |
+-------------+
| $.f2[1].val |
+-------------+
1 row in set (0.00 sec)
mysql> SELECT JSON_SET(#`json`, #`path`, 'd');
+-------------------------------------------------------------------------------------------------------------------+
| JSON_SET(#`json`, #`path`, 'd') |
+-------------------------------------------------------------------------------------------------------------------+
| {"f1": "abc", "f2": [{"id": 1, "val": "a"}, {"id": 2, "val": "d"}, {"id": 3, "val": "c"}], "f3": ["a", "b", "c"]} |
+-------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
See db-fiddle.
Related
I struggle to extract the required values from the JSON column in MySQL.
Imagine the variable:
SET #j = '[{"a": 1, "b": 2},{"a": 3, "b": 4, "c": {"d": 4}}, {"a": 5}]';
and I need to extract and return a new JSON array containing only selected key-value pairs, eg.: only a's and b's and if the required key does not exist I want to return null for that key:
[{"a": 1, "b": 2},{"a": 3, "b": 4},{"a": 5, "b": null}]
thanks
Here's a solution using MySQL 8.0's JSON_TABLE() function:
mysql> SET #j = '[{"a": 1, "b": 2},{"a": 3, "b": 4, "c": {"d": 4}}, {"a": 5}]';
Query OK, 0 rows affected (0.00 sec)
mysql> select j.* from json_table(#j, '$[*]'
columns (a int path '$.a', b int path '$.b')) as j;
+------+------+
| a | b |
+------+------+
| 1 | 2 |
| 3 | 4 |
| 5 | NULL |
+------+------+
3 rows in set (0.00 sec)
Based on this result, we can build a new JSON array of objects:
mysql> select json_arrayagg(json_object('a', j.a, 'b', j.b)) as new
from json_table(#j, '$[*]'
columns (a int path '$.a', b int path '$.b')) as j;
+-----------------------------------------------------------+
| new |
+-----------------------------------------------------------+
| [{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": null}] |
+-----------------------------------------------------------+
If you are using MySQL 5.x, I would recommend just fetch the whole JSON document to a client application where you can deserialize it into data structures to manipulate in code.
I have JSON like:
{"1": "6", "2": "10", "3": "12"}
And i would like to get LAST key and value using MySQL query to get output like:
3x12
3 is the last key, and 12 is the last key value...
Is there any MySQL query to do that? I know using reading whole MySQL filed value as posted above and then while loop and if key is last print its value and key...but if is possible in MySQL query to get this output?
I im using this php that reads MySQL field value and get last key and value...but i don't know how to do it in mysql:
$json = json_decode('{"1": "6", "2": "10", "3": "12"}', true);
$value = end($json);
$key = key($json);
echo 'KEY: '.$key.'...VALUE: '.$value;
You can try something like the following, adjust as necessary:
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.20 |
+-----------+
1 row in set (0.00 sec)
mysql> SET #`json` := '
'> {
'> "1": "6",
'> "2": "10",
'> "3": "12"
'> }
'> ';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT
-> CONCAT(
-> JSON_UNQUOTE(#`key`),
-> 'x',
-> JSON_UNQUOTE(
-> JSON_EXTRACT(#`json`,
-> CONCAT('$.', #`key`)
-> )
-> )
-> ) `value`
-> FROM (
-> SELECT #`key` := JSON_EXTRACT(
-> JSON_KEYS(#`json`),
-> CONCAT('$[', JSON_LENGTH(#`json`) - 1, ']')
-> )
-> ) `init`;
+-------+
| value |
+-------+
| 3x12 |
+-------+
1 row in set (0.00 sec)
See db-fiddle.
I have a database with a JSON array in column extra_fields. I need to extract one value by id.
The JSONs looks something like this, though the number of objects is random for each row:
[
{"id":"1","value":"48768"},
{"id":"2","value" ["String","http:someurl"]},
{"id":"5","value":"somevalue"},
{"id":"6","value":""},
{"id":"8","value":"Op-Ed"},
{"id":"9","value":"8111,13498,15408"},
{"id":"10","value":"30"},
{"id":"11","value":"This is the target string"}
]
I can extract an array of ids with:
SELECT extra_fields->>"$[*].id" FROM esqt7_k2_items;
I can extract an array of values with:
SELECT extra_fields->>"$[*].value" FROM esqt7_k2_items;
I can extract a single value at the nth zero-ordered object position with:
SELECT extra_fields->>"$[2].value" FROM esqt7_k2_items;
But the problem is that there's a variable number of objects and I specifically need the value of object id = 11. JSON_EXTRACT doesn't seem to support filtering, as every variation of a JSONPATH seems to fail. $..[?(#.id=11)].value works as a JSONPATH, but this (and many variants) fails:
SELECT extra_fields->"$.[?(#.id=11)].value" FROM esqt7_k2_items; #FAILS
This is a migration project and the JSON stuff is just how Joomla chose to implement their extra fields. Kind of a pain if you ask me.
One option you can consider (be careful about performance issues):
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.19 |
+-----------+
1 row in set (0.00 sec)
mysql> DROP TABLE IF EXISTS `esqt7_k2_items`;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS `esqt7_k2_items` (
-> `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> `extra_fields` JSON NOT NULL
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO `esqt7_k2_items`
-> (`extra_fields`)
-> VALUES
-> ('
'> [
'> {"id": "1", "value": "48768"},
'> {"id": "2", "value": ["String","http:someurl"]},
'> {"id": "5", "value": "somevalue"},
'> {"id": "6", "value": ""},
'> {"id": "8", "value": "Op-Ed"},
'> {"id": "9", "value": "8111,13498,15408"},
'> {"id": "10", "value": "30"},
'> {"id": "11", "value": "This is the target string"}
'> ]
'> '),
-> ('
'> [
'> {"id": "1", "value": ""},
'> {"id": "9", "value": "ONE This is the target string"}
'> ]
'> '),
-> ('
'> [
'> {"id": "6", "value": ""},
'> {"id": "11", "value": "TWO This is the target string"}
'> ]
'> ');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> SET #`search_id` := '11';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT `der`.`id`, `der`.`value`
-> FROM (
-> SELECT
-> `id`,
-> JSON_UNQUOTE(
-> JSON_EXTRACT(`extra_fields`,
-> CONCAT((SELECT
-> JSON_UNQUOTE(
-> JSON_SEARCH(`extra_fields` ->> '$[*].id', 'one', #`search_id`)
-> )
-> FROM `esqt7_k2_items` `esqt7_k2_items_in`
-> WHERE `esqt7_k2_items_out`.`id` = `esqt7_k2_items_in`.`id`
-> ), ".value"
-> )
-> )
-> ) `value`
-> FROM `esqt7_k2_items` `esqt7_k2_items_out`
-> ) `der`
-> WHERE `der`.`value` IS NOT NULL;
+----+-------------------------------+
| id | value |
+----+-------------------------------+
| 1 | This is the target string |
| 3 | TWO This is the target string |
+----+-------------------------------+
2 rows in set (0.00 sec)
See db-fiddle.
Simpliest way i found to get
the value of object id = 11
its by finding the right "index" of the needed object, doing a bit of PHP.
Firstly we get all objects Ids, by your simple request:
> SELECT extra_fields->>"$[*].id" FROM esqt7_k2_items;
then in PHP
$rightIndex = array_search($ids, '11');
and then you get the right object by passing this $rightIndex :
SELECT extra_fields->>"$[$rightIndex].value" FROM esqt7_k2_items;
Simple as hell! The same thing you can do anywhere else, for example, when you need to UPDATE an object in array, or its value/values, etc..
That is, Cheers!
Given the following JSON object stored in MariaDB/MySQL:
SET #j = '{
"thing": {
"sub_things": [
{
"attribute": [
{ "1": 40 },
{ "5": 25 },
{ "13": 35 }
]
},
{
"attribute": [
{ "2": 50 },
{ "7": 50 }
]
}
]
}
}'
How would I query this to return sub_things where one of the objects in the attribute array has a particular key e.g. where the key is 13 this should return the first sub_thing.
Thanks!
I'm not sure if in a single query you can achieve what you need.
The following stored procedure, perhaps, can give you some ideas (stored procedure works for MariaDB and for MySQL):
> DROP PROCEDURE IF EXISTS `JSON_query_based_on_key`;
Query OK, 0 rows affected (0.01 sec)
> DELIMITER //
> CREATE PROCEDURE `JSON_query_based_on_key`(
-> `json` TEXT,
-> `key` VARCHAR(5)
-> )
-> BEGIN
-> DECLARE `sub_things_current` INT
-> DEFAULT JSON_LENGTH(`json`, '$.thing.sub_things') - 1;
->
-> WHILE (`sub_things_current` > -1) DO
-> IF NOT JSON_CONTAINS_PATH(
-> `json`,
-> 'one',
-> CONCAT('$.thing.sub_things[', `sub_things_current`, '].attribute[*]."', `key`, '"')
-> ) THEN
-> SET `json` := JSON_REMOVE(
-> `json`,
-> CONCAT('$.thing.sub_things[', `sub_things_current`, ']'));
-> END IF;
-> SET `sub_things_current` := `sub_things_current` - 1;
-> END WHILE;
->
-> SELECT JSON_EXTRACT(`json`, '$.thing');
-> END//
Query OK, 0 rows affected (0.00 sec)
> DELIMITER ;
> CALL `JSON_query_based_on_key`('{
'> "thing": {
'> "sub_things": [
'> {
'> "attribute": [
'> { "1": 40 },
'> { "5": 25 },
'> { "13": 35 }
'> ]
'> },
'> {
'> "attribute": [
'> { "2": 50 },
'> { "7": 50 }
'> ]
'> }
'> ]
'> }
'> }', '13');
+---------------------------------------------------------------------+
| JSON_EXTRACT(`json`, '$.thing') |
+---------------------------------------------------------------------+
| {"sub_things": [{"attribute": [{"1": 40}, {"5": 25}, {"13": 35}]}]} |
+---------------------------------------------------------------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Modify the code as you need it.
See db<>fiddle for MariaDB (10.2.6) and db-fiddle for MySQL (5.7.17).
I've got a json list of objects like that
[{
"something": "bla",
"id": 2
}, {
"something": "yes",
"id": 1
}]
My id field is always a numeric value. But when I try to find id = 2, MySQL returns NULL
select
json_search(
json_extract(
'[{"something": "bla" ,"id": 2}, {"something": "yes","id": 1}]',
"$[*].id"
),
'one',
2
) as json_search;
json_search |
------------|
|
When I use a string as value in my json id object instead of a numeric value, I got a result with Index 0.
select
json_search(
json_extract(
'[{"something": "bla" ,"id": "2"}, {"something": "yes","id": 1}]',
"$[*].id"
),
'one',
"2"
) as json_search;
json_search |
------------|
"$[0]" |
I'm using MySQL 5.7.17
##version |
-----------|
5.7.17-log |
Is numeric search in json arrays not provided in MySQL?
You can try something complicated, not intuitive and possibly with performance problems, but it's an option:
mysql> SELECT JSON_SEARCH(
-> REPLACE(
-> REPLACE(
-> REPLACE(
-> JSON_EXTRACT('[
'> {"something": "bla" ,"id": 2},
'> {"something": "yes","id": 1}
'> ]', "$[*].id"),
-> ', ', '","'),
-> '[', '["'),
-> ']', '"]'),
-> 'one', '2') `json_search`;
+-------------+
| json_search |
+-------------+
| "$[0]" |
+-------------+
1 row in set (0.00 sec)
Although the JSON_EXTRACT function was returning [2, 1] and it was a valid JSON, if you search the documentation the JSON_SEARCH function is:
JSON_SEARCH(json_doc, one_or_all, search_str[, escape_char[, path] ...])
So, as I understood you can only work with STRING values, and not with numeric values. But one solution to your problem could be to use the JSON_CONTAINS function as it returns 1 or 0 if the numeric value exists or not.
select
json_contains(
json_extract(
'[{"something": "bla" ,"id": 2}, {"something": "yes","id": 1}]',
"$[*].id"
),
"2"
) as json_contains;
The only problem is that you could not get the path to the given value within the JSON document. Hope it helped.