Reading JSON from MEMSQL - json

Having a problem with JSON / MEMSQL. Here's my table:
CREATE TABLE `MEMSQLPOLYGLOT` (
ID BIGINT AUTO_INCREMENT,
`DATA` JSON NULL,
PRIMARY KEY (ID)
);
Here's the record I'm trying to read:
insert into MEMTEST (DATA) values
('
{
"EnterpriseMessage" :
{
"Body" :
[
{
"AccountNumber":"ABCD",
"AdminCompany":null,
"BrokerNumber":"WWonka"
},
{
"AccountNumber":"CSNE",
"AdminCompany":null,
"BrokerNumber":"ZWiza"
}
],
"Header" :
{
"mimetye":"application/vnd.ms-powerpoint",
"destinationsystem":"ETL",
"requiresack":"FALSE",
"SimpArr":
[
"BYTS6181",
"EVU98124",
"Genesys"
],
"EmptyFile":1
}
}
}
');
I can read the SimpArr array in the header.
SELECT DATA::EnterpriseMessage::Header::SimpArr from MEMTEST;
Returns:
["BYTS6181","EVU98124","Genesys"]
I can also pass in a key index to get a specific value, such as:
select JSON_EXTRACT_JSON(DATA::EnterpriseMessage::Body, 1) from MEMTEST;
This returns the 2nd value of the SimpArr since it's a zero-based index.
I can also read the array of objects in the Body:
select JSON_EXTRACT_JSON(DATA::EnterpriseMessage::Body, 0) from MEMTEST;
which returns the first object in that array:
{
"AccountNumber":"ABCD",
"AdminCompany":null,
"BrokerNumber":"WWonka"
}
However, I am unable to find a way to read the individual attributes of this object.
Anyone have any ideas?

You can just pass the key as an extra argument to JSON_EXTRACT_JSON:
mysql> select JSON_EXTRACT_JSON(DATA::EnterpriseMessage::Body, 0, "AccountNumber") from MEMTEST;
+----------------------------------------------------------------------+
| JSON_EXTRACT_JSON(DATA::EnterpriseMessage::Body, 0, "AccountNumber") |
+----------------------------------------------------------------------+
| "ABCD" |
+----------------------------------------------------------------------+
1 row in set (0.08 sec)
JSON_EXTRACT_JSON takes any number of arguments, either string keys or integer array indices. For example, we can unroll the :: syntax in your example above:
select JSON_EXTRACT_JSON(DATA, "EnterpriseMessage", "Body", 0, "AccountNumber") from MEMTEST;
Also note that the return value has double quotes in it. That's because you extract JSON, and the JSON representation of a string has double quotes. If you actually want to get the string, use JSON_EXTRACT_STRING:
mysql> select JSON_EXTRACT_STRING(DATA, "EnterpriseMessage", "Body", 0, "AccountNumber") from MEMTEST;
+----------------------------------------------------------------------------+
| JSON_EXTRACT_STRING(DATA, "EnterpriseMessage", "Body", 0, "AccountNumber") |
+----------------------------------------------------------------------------+
| ABCD |
+----------------------------------------------------------------------------+
1 row in set (0.07 sec)

Related

List in a nested json in mariaDB with json_table

I can't get the data from a nested json using json_table
My MariaDB server is 10.6.0
set #json='
{
"id":"9696e1ac-e173-4905-ac51-c47c0ef8c90b",
"username":"demo#demo.com",
"firstName":"DEMO",
"lastName":"DEMO",
"email":"demo#demo.com",
"isEnabled":true,
"attributes":{
"ASSOCIATED_COMPANY":"CLCORP001,CLCORP002,CLCORP003,CLCORP004,PECORP001,PECORP002,PECORP003,PECORP004,PECORP005,DECORP001",
"ROLES":"[\"Super Admin\",\"Admin\",\"Executive\"]",
},
}';
I tried this but it doesn't work
SELECT
*
FROM
SELECT * FROM json_table(#json, '$.*' COLUMNS(NESTED PATH '$.attributes.ROLES[*]' COLUMNS (roles varchar(250) PATH '$'))) roles
TEXT PATH '$'))) roles
No matter if MySQL or MariaDB: If your json string is not valid, json_object will always fail.
In both MySQL and MariaDB you can use json_valid() function to check your json:
set #json='
{
"id":"9696e1ac-e173-4905-ac51-c47c0ef8c90b",
"username":"demo#demo.com",
"firstName":"DEMO",
"lastName":"DEMO",
"email":"demo#demo.com",
"isEnabled":true,
"attributes":{
"ASSOCIATED_COMPANY":"CLCORP001,CLCORP002,CLCORP003,CLCORP004,PECORP001,PECORP002,PECORP003,PECORP004,PECORP005,DECORP001",
"ROLES":"[\"Super Admin\",\"Admin\",\"Executive\"]",
},
}';
SELECT JSON_VALID(#json);
+-------------------+
| JSON_VALID(#json) |
+-------------------+
| 0 |
+-------------------+
On success JSON_VALID will return 1.
I didn't check in detail, but looks like the extra quotation marks in attributes.ROLES are not allowed.

Counting words in JSON array mySQL

I need to count true and false words in a JSON datatype.
I have this JSON in the cell:
{"1": true, "2": false, "3": true}
The number of values may vary. I realize that I can count the total number of values in the array but how can I count true and false separately?
For total count I used JSON_LENGTH()
One option would be using below approach containing JSON_LENGTH(), JSON_EXTRACT() and JSON_SEARCH() functions together even for the version 5.7 (5.7.13+) where an array(js) extracted in the subquery and they're splitted to individual array for each values (true and false) by using JSON_SEARCH() function containing all as the second argument, and then counted by JSON_LENGTH() function :
SELECT ID,
JSON_LENGTH( JSON_SEARCH(js, 'all', 'true') ) AS Cnt_True,
JSON_LENGTH( JSON_SEARCH(js, 'all', 'false') ) AS Cnt_False
FROM ( SELECT *, JSON_EXTRACT(jsdata, '$.*') AS js
FROM tab ) t
provided JSON field has quoted values such as "true" and "false"
JSON_EXTRACT(jsdata, '$.*') still can be used in case that the boolean values are unquoted as in your case. But, this time some string operations would be needed. Here, I preferred using CHAR_LENGTH() function :
SELECT ID,
CHAR_LENGTH(js) - CHAR_LENGTH(REPLACE(js, 'true', SPACE(LENGTH('true')-1)))
AS Cnt_True,
CHAR_LENGTH(js) - CHAR_LENGTH(REPLACE(js, 'false', SPACE(LENGTH('false')-1)))
AS Cnt_False
FROM
( SELECT *, JSON_EXTRACT(jsdata, '$.*') AS js
FROM tab ) t
Demo
Here is one option using json_table(), availabe in MySQL 8.0.
You can first turn each json object to an array of values using path $.*'. Then, you can pass the resulting json array to json_table(), which will put each value on a separate row. The final step is conditional aggregation.
Assuming that the json column is called js, that would be:
select sum(x.val = 'true') cnt_true, sum(x.val = 'false') cnt_false
from mytable t
cross join json_table(js -> '$.*', '$[*]' columns (val varchar(5) path '$')) x
Demo on DB Fiddle
Sample data (I added another row to make this more meaningful):
| js |
| :--------------------------------- |
| {"1": true, "2": false, "3": true} |
| {"bar": false, "foo": true} |
Results:
cnt_true | cnt_false
-------: | --------:
3 | 2

MySQL JSON object insert

I have the following JSON:
{
"params" : {
"A" : 200.5,
"B" : 70.2
}
}
And the following table:
CREATE TABLE `params` (
`param` varchar(255),
`value` float
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Is there a way to make mysql function with one insert query without using "while do" directly to insert all the parameters into table like this:
-------+-------
| param | value |
|-------+-------|
| A | 200.5|
| B | 70.2|
---------------
If you are running MySQL 8.0, you can use json_keys() to dynamically extract the keys from the json subobject as a json array, and then use json_table() to turn it to rows. You can then extract the values.
Consider:
insert into `params`
with t as (select '{"params": { "A": 200.5, "B": 70.2 } }' js)
select x.k, json_extract(js, concat('$.params.', x.k)) v
from
t
cross join json_table(
json_keys(js->"$.params"),
"$[*]" columns(k varchar(255) path "$")
) as x
Demo on DB Fiddle
Content of the table after running the query:
param | value
:---- | ----:
A | 200.5
B | 70.2
We can insert multiple rows with a single query using insert into. we need to construct this query using your JSON object
INSERT INTO params(param,value)
VALUES('A',200.5), ('B', 70.2);

Extracting value using common_schema not working for nested JSON

I have a DB field called data of type is MEDIUMTEXT, which stores values in JSON format. I'm using extract_json_value method from common_schema.
When JSON is not nested, it works fine. For example, when applications_data table's data field is
{
"key": "value"
}
This query works fine:
SELECT data into #json from applications_data;
SELECT common_schema.extract_json_value(#json, 'key') as result;
and gives result: key
However, when the data field is a nested JSON, it fails. For example, JSON is:
{
"key": {
"overview": "sample"
}
}
Using the same query as above and result is empty, not NULL:
Remember:
extract_json_value
...
This function internally relies on json_to_xml(): it first
converts the JSON data to XML, then uses ExtractValue to apply
XPath.
...
and
ExtractValue(xml_frag, xpath_expr)
...
If no matching text node is found for the expression (including the
implicit /text())—for whatever reason, as long as xpath_expr is valid, and xml_frag consists of elements which are properly nested and closed—an empty string is returned. No distinction is made between a match on an empty element and no match at all. This is by design.
If you need to determine whether no matching element was found in
xml_frag or such an element was found but contained no child text
nodes, you should test the result of an expression that uses the XPath
count() function.
...
Test:
mysql> SET #`json` := '
'> {
'> "key": {
'> "overview": "sample"
'> }
'> }
'> ';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT
-> common_schema.extract_json_value(#`json`, 'key') AS result0,
-> common_schema.extract_json_value(#`json`, count('key')) AS result1,
-> common_schema.extract_json_value(#`json`, 'key/overview') AS result2,
-> common_schema.extract_json_value(#`json`, count('key/overview')) AS result3;
+---------+---------+---------+---------+
| result0 | result1 | result2 | result3 |
+---------+---------+---------+---------+
| | 1 | sample | 1 |
+---------+---------+---------+---------+
1 row in set (0.03 sec)

Update every value in an array in postgres json

In my postgres database I have json that looks similar to this:
{
"myArray": [
{
"myValue": 1
},
{
"myValue": 2
},
{
"myValue": 3
}
]
}
Now I want to rename myValue to otherValue. I can't be sure about the length of the array! Preferably I would like to use something like set_jsonb with a wildcard as the array index, but that does not seem to be supported. So what is the nicest solution?
You have to decompose a whole jsonb object, modify individual elements and build the object back.
The custom function will be helpful:
create or replace function jsonb_change_keys_in_array(arr jsonb, old_key text, new_key text)
returns jsonb language sql as $$
select jsonb_agg(case
when value->old_key is null then value
else value- old_key || jsonb_build_object(new_key, value->old_key)
end)
from jsonb_array_elements(arr)
$$;
Use:
with my_table (id, data) as (
values(1,
'{
"myArray": [
{
"myValue": 1
},
{
"myValue": 2
},
{
"myValue": 3
}
]
}'::jsonb)
)
select
id,
jsonb_build_object(
'myArray',
jsonb_change_keys_in_array(data->'myArray', 'myValue', 'otherValue')
)
from my_table;
id | jsonb_build_object
----+------------------------------------------------------------------------
1 | {"myArray": [{"otherValue": 1}, {"otherValue": 2}, {"otherValue": 3}]}
(1 row)
Using json functions are definitely the most elegant, but you can get by on using character replacement. Cast the json(b) as text, perform the replace, then change it back to json(b). In this example I included the quotes and colon to help the text replace target the json keys without conflict with values.
CREATE TABLE mytable ( id INT, data JSONB );
INSERT INTO mytable VALUES (1, '{"myArray": [{"myValue": 1},{"myValue": 2},{"myValue": 3}]}');
INSERT INTO mytable VALUES (2, '{"myArray": [{"myValue": 4},{"myValue": 5},{"myValue": 6}]}');
SELECT * FROM mytable;
UPDATE mytable
SET data = REPLACE(data :: TEXT, '"myValue":', '"otherValue":') :: JSONB;
SELECT * FROM mytable;
http://sqlfiddle.com/#!17/1c28a/9/4