MySQL select last key with value JSON - mysql

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.

Related

json_search throwing an error MySQL - #2014 - Commands out of sync; you can't run this command now

I am using json_search to search the json formatted data from the column. For normal json data, it's working fine. But it's not working if string has a double quote.
MySQL Query
SELECT JSON_SEARCH(user_information, 'one', '%name%', null, '$[*].label');
It's working fine for regular json data but it's not working for below data which is stored in the single column database.
Data Example on which query is not working:
[{
"name":"John Smith",
"value":"John",
"label":"Sm"ith"
}]
Here is db-fiddle for this example.
As you can see in the data, for label key the string is sm"ith, I've just added one double quote middle of the string and then it's showing below error on db-fiddle.
Invalid JSON text in argument 1 to function json_search: "Missing a
comma or '}' after an object member."
But on my local host it's showing below error.
2014 - Commands out of sync; you can't run this command now
You must use the double backslash escape sequence, see Creating JSON Values.
mysql> SELECT JSON_VALID('
'> [
'> {
'> "name":"John Smith",
'> "value":"John",
'> "label":"Sm"ith"
'> }
'> ]
'> ') `is_json_valid?`;
+----------------+
| is_json_valid? |
+----------------+
| 0 |
+----------------+
1 row in set (0.00 sec)
mysql> SELECT JSON_VALID('
'> [
'> {
'> "name":"John Smith",
'> "value":"John",
'> "label":"Sm\\"ith"
'> }
'> ]
'> ') `is_json_valid?`;
+----------------+
| is_json_valid? |
+----------------+
| 1 |
+----------------+
1 row in set (0.00 sec)

Updateing mysql json field in array

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.

MySQL JSON extract a single value by id

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!

Importing Json field from csv to MySQL 5.7.19-0ubuntu0.16.04.1

I have MySQL table consisting of 3 columns having type of (cookie vachar, userdata json, userprefernce json). I am trying to load the data from csv but it gives the following error :
Error:
Error Code: 3140 Invalid JSON text: Invalid value at position 0 in value for column userdata'
I have tried to validate the JSON it looks fine. Now I couldn't understand what is the problem
Sample Row:
ZwpBHCrWObHE61rSOpp9dkUfJ, '{"bodystyle": {"SUV/MUV": 2}, "budgetsegment": {"EP": 2}, "models": {"Grand Cherokee": 1, "XC90": 1}}', '{"bodystyle": "SUV/MUV", "budgetsegment": "EP", "models": "Grand Cherokee,XC90"}'
I can't reproduce the problem:
File: /path/to/file/data.csv
ZwpBHCrWObHE61rSOpp9dkUfJ, '{"bodystyle": {"SUV/MUV": 2}, "budgetsegment": {"EP": 2}, "models": {"Grand Cherokee": 1, "XC90": 1}}', '{"bodystyle": "SUV/MUV", "budgetsegment": "EP", "models": "Grand Cherokee,XC90"}'
MySQL Command Line:
mysql> \! lsb_release --description
Description: Ubuntu 16.04.2 LTS
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.19 |
+-----------+
1 row in set (0.00 sec)
mysql> DROP TABLE IF EXISTS `table`;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS `table` (
-> `cookie` VARCHAR(25) NOT NULL,
-> `userdata` JSON NOT NULL,
-> `userprefernce` JSON NOT NULL
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> LOAD DATA LOCAL INFILE '/path/to/file/data.csv'
-> INTO TABLE `table`
-> FIELDS TERMINATED BY ', '
-> OPTIONALLY ENCLOSED BY '\''
-> LINES TERMINATED BY '\n';
Query OK, 1 row affected (0.00 sec)
Records: 1 Deleted: 0 Skipped: 0 Warnings: 0
mysql> SELECT
-> `cookie`,
-> `userdata`,
-> `userprefernce`
-> FROM
-> `table`\G
*************************** 1. row ***************************
cookie: ZwpBHCrWObHE61rSOpp9dkUfJ
userdata: {"models": {"XC90": 1, "Grand Cherokee": 1}, "bodystyle": {"SUV/MUV": 2}, "budgetsegment": {"EP": 2}}
userprefernce: {"models": "Grand Cherokee,XC90", "bodystyle": "SUV/MUV", "budgetsegment": "EP"}
1 row in set (0.00 sec)

MySQL json_search on numeric values

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.