mysql - avoid escaping double quotes in json functions - mysql

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))

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|
+-------+---------+---------+

Is there an opposite of MySQL's JSON_ARRAY_APPEND?

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.

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

Postgresql merge rows with same key (hstore or json)

I have a table like this:
+--------+--------------------+
| ID | Attribute |
+--------+--------------------+
| 1 |"color" => "red" |
+--------+--------------------+
| 1 |"color" => "green" |
+--------+--------------------+
| 1 |"shape" => "square" |
+--------+--------------------+
| 2 |"color" => "blue" |
+--------+--------------------+
| 2 |"color" => "black" |
+--------+--------------------+
| 2 |"flavor" => "sweat" |
+--------+--------------------+
| 2 |"flavor" => "salty" |
+--------+--------------------+
And I want to run some postgres query that get a result table like this:
+--------+------------------------------------------------------+
| ID | Attribute |
+--------+------------------------------------------------------+
| 1 |"color" => "red, green", "shape" => "square" |
+--------+------------------------------------------------------+
| 2 |"color" => "blue, black", "flavor" => "sweat, salty" |
+--------+------------------------------------------------------+
The attribute column can either be hstore or json format. I wrote it in hstore for an example, but if we cannot achieve this in hstore, but in json, I would change the column to json.
I know that hstore does not support one key to multiple values, when I tried some merge method, it only kept one value for each key. But for json, I didn't find anything that supports multiple value merge like this neither. I think this can be done by function merging values for the same key into a string/text and add it back to the key/value pair. But I'm stuck in implementing it.
Note: if implement this in some function, ideally any key such as color, shape should not appear in the function since keys can be expanded dynamically.
Does anyone have any idea about this? Any advice or brainstorm might help. Thank you!
Just a note before anything else: in your desidered output I would use some proper json and not that kind of lookalike. So a correct output according to me would be:
+--------+----------------------------------------------------------------------+
| ID | Attribute |
+--------+----------------------------------------------------------------------+
| 1 | '{"color":["red","green"], "flavor":[], "shape":["square"]}' |
+--------+----------------------------------------------------------------------+
| 2 | '{"color":["blue","black"], "flavor":["sweat","salty"], "shape":[]}' |
+--------+----------------------------------------------------------------------+
A PL/pgSQL function which parses the json attributes and executes a dynamic query would do the job, something like that:
CREATE OR REPLACE FUNCTION merge_rows(PAR_table regclass) RETURNS TABLE (
id integer,
attributes json
) AS $$
DECLARE
ARR_attributes text[];
VAR_attribute text;
ARR_query_parts text[];
BEGIN
-- Get JSON attributes names
EXECUTE format('SELECT array_agg(name ORDER BY name) AS name FROM (SELECT DISTINCT json_object_keys(attribute) AS name FROM %s) AS s', PAR_table) INTO ARR_attributes;
-- Write json_build_object() query part
FOREACH VAR_attribute IN ARRAY ARR_attributes LOOP
ARR_query_parts := array_append(ARR_query_parts, format('%L, array_remove(array_agg(l.%s), null)', VAR_attribute, VAR_attribute));
END LOOP;
-- Return dynamic query
RETURN QUERY EXECUTE format('
SELECT t.id, json_build_object(%s) AS attributes
FROM %s AS t,
LATERAL json_to_record(t.attribute) AS l(%s)
GROUP BY t.id;',
array_to_string(ARR_query_parts, ', '), PAR_table, array_to_string(ARR_attributes, ' text, ') || ' text');
END;
$$ LANGUAGE plpgsql;
I've tested it and it seems to work, it returns a json with. Here is my test code:
CREATE TABLE mytable (
id integer NOT NULL,
attribute json NOT NULL
);
INSERT INTO mytable (id, attribute) VALUES
(1, '{"color":"red"}'),
(1, '{"color":"green"}'),
(1, '{"shape":"square"}'),
(2, '{"color":"blue"}'),
(2, '{"color" :"black"}'),
(2, '{"flavor":"sweat"}'),
(2, '{"flavor":"salty"}');
SELECT * FROM merge_rows('mytable');
Of course you can pass the id and attribute column names as parameters as well and maybe refine the function a bit, this is just to give you an idea.
EDIT : If you're on 9.4 please consider using jsonb datatype, it's much better and gives you room for improvements. You would just need to change the json_* functions to their jsonb_* equivalents.
If you just want this for display purposes, this might be enough:
select id, string_agg(key||' => '||vals, ', ')
from (
select t.id, x.key, string_agg(value, ',') vals
from t
join lateral each(t.attributes) x on true
group by id, key
) t
group by id;
If you are not on 9.4, you can't use the lateral join:
select id, string_agg(key||' => '||vals, ', ')
from (
select id, key, string_agg(val, ',') as vals
from (
select t.id, skeys(t.attributes) as key, svals(t.attributes) as val
from t
) t1
group by id, key
) t2
group by id;
This will return:
id | string_agg
---+-------------------------------------------
1 | color => red,green, shape => square
2 | color => blue,black, flavor => sweat,salty
SQLFiddle: http://sqlfiddle.com/#!15/98caa/2