Cast JSON array values in MySQL - mysql

I have a MySQL JSON column containing values such as:
[33173,33386,24272,33499,33526,33347]
Would it be possible, with JSON functions, to cast each value in the array to string? The output would be:
["33173","33386","24272","33499","33526","33347"]
I'd like to avoid resorting to dirty REPLACE() calls.

If you use MySQL 8.0, you can use the JSON_TABLE() function to explode the array into rows, then cast them to CHAR(), then JSON_ARRAYAGG() to implode the rows back into a JSON array.
set #j = '[33173,33386,24272,33499,33526,33347]';
select json_arrayagg(cast(i as char(5))) as j
from json_table(#j, '$[*]' columns (i int path '$')) j;
Output:
+--------------------------------------------------------+
| j |
+--------------------------------------------------------+
| ["33173", "33386", "24272", "33499", "33526", "33347"] |
+--------------------------------------------------------+

Related

How to order by array value inside json path mysql?

I have a column in db table which is of a JSON type.
This column will store such values:
column 1: ["bmw", "opel", "audi"]
column 2: ["opel", "bwm", "audi"]
column 3: ["audi"]
the order of values is different as well as the total number but in general there is a list of valid values to be stored.
On UI, there is a table with columns which can be sorted. When user clicks on a column Car Brand, ASC, I will need to sort the output from db based on the column values I mentioned above.
I'd like to receive the following output:
1: ["audi"]
2: ["audi", "bmw", "opel"]
3: ["audi", "bmw", "opel"]
I can't seem to find the solution for this using JSON path.
Can anybody help me?
Please do not suggest to store the values in a different way, it's not me who decides this.
You can't do this in MySQL.
What Akina describes in the comment above is like this demo:
set #j = '["opel", "bwm", "audi"]';
select * from json_table(#j, '$[*]' columns (a varchar(10) path '$')) j order by a;
+------+
| a |
+------+
| audi |
| bwm |
| opel |
+------+
Then the idea is to recombine these rows into a JSON array using JSON_ARRAYAGG(). However, this doesn't work:
select json_arrayagg(a) as j from (
select * from json_table(#j, '$[*]' columns (a varchar(10) path '$')) j order by a
) as a;
+-------------------------+
| j |
+-------------------------+
| ["opel", "bwm", "audi"] |
+-------------------------+
The ORDER BY in the subquery has been factored out. MySQL thinks there's no reason to sort rows in a derived table, they should be sorted as the last step of the outer query.
https://dev.mysql.com/doc/refman/5.7/en/aggregate-functions.html#function_json-arrayagg says:
JSON_ARRAYAGG(col_or_expr)
Aggregates a result set as a single JSON array whose elements consist
of the rows. The order of elements in this array is undefined.
https://bugs.mysql.com/bug.php?id=94696 is a feature request from 2019, asking for a way to sort elements before aggregating them with JSON_ARRAYAGG(). That ticket is in a "Verified" state, meaning it has been accepted as a feature request, but it has not been implemented.
There is no solution yet in MySQL 8.0 to use JSON_ARRAYAGG() to produce an array in a specific order.
So the best you can do is fetch them as sorted rows, as I showed in the intermediate step earlier in this answer, and then combine them into an array in that order in your client application code.

MySQL access string like an array

I'm was trying to make function that will return random character. What I have is a string that contains alphanumerics. In Postgresql I can set text as array, but I don't know how MySQL do same. for example:
in postgresql
DECLARE chars TEXT[] := '{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}';
So when I want get chars[10] it will return A. How to do this in MySQL?
I think the questoin is not about arrays, it is about get random char, right? In this case You can use next approach in MySQL:
SELECT SUBSTRING(
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
ROUND(RAND()*61)+1,
1
) as random_char;
The SUBSTRING function returns one char from random position.
Try it on SQLize.online
Starting MySQL 5.7, one option is to use a JSON array:
set #chars = '["0", "1", "A", "B"]';
select json_unquote(json_extract(#chars, '$[2]')) as value_at_index_2;
Yields:
| value_at_index_2 |
| :--------------- |
| A |

MySQL Return JSON array index based on property value

I have a table with JSON data like this:
{"a": [{"color": "blue", "value": 15}, {"color": "red", "value": 30}]}
I need to get the "value" that is inside the same object of "blue".
I thought to use the code below:
SELECT JSON_EXTRACT(my_data, '$.a[0].value');
The problem is that the "blue" object can be in any index of the array.
So, is there a way to retrieve the index first and then i'll query using the right index?
UPDATE
The Barmar's answer works but it needs to wrap in JSON_UNQUOTE()
Use JSON_SEARCH() to find the path to blue.
SELECT JSON_EXTRACT(my_data, JSON_UNQUOTE(REPLACE(JSON_SEARCH(my_data, 'one', 'blue'), '.color', '.value')))
JSON_SEARCH will return a string like $.a[0].color. REPLACE changes that to $.a[0].value, then you extract that element.
DEMO
Here's an example of using JSON_TABLE():
select j.* from d, json_table(d.data, '$.a[*]' columns (
color varchar(20) path '$.color',
value int path '$.value')
) as j;
+-------+-------+
| color | value |
+-------+-------+
| blue | 15 |
| red | 30 |
+-------+-------+
You can then apply conditions in the WHERE clause, as if you had stored the data in a normal table.
select j.* from d, json_table(d.data, '$.a[*]' columns (
color varchar(20) path '$.color',
value int path '$.value')
) as j
where j.color = 'blue';
+-------+-------+
| color | value |
+-------+-------+
| blue | 15 |
+-------+-------+
This requires you to write a complex query like this EVERY TIME you query the JSON data.
One wonders if it would have been easier to store the JSON in a normal table from the start.
I often recommend to MySQL users that storing data as JSON makes more work for you, if you need to make SQL expressions to reference individual fields within the JSON. I wouldn't use JSON in these cases, I'd explode the JSON array into rows, and the JSON fields into columns of a set of normal tables. Then you can write simpler queries, you can optimize with indexes, and you can use constraints and data types properly.
JSON is the most easily misused feature of the recent MySQL releases.

How can I convert the string values inside a MySQL JSON array to upper case?

I have a table that contains a JSON column, and in it a JSON array:
mysql> SELECT profile->'$.countriesVisited' from users;
+-------------------------------+
| profile->'$.countriesVisited' |
+-------------------------------+
| ["us", "il"] |
| ["co", "ph"] |
+-------------------------------+
2 rows in set (0.00 sec)
I want to convert the values inside the array into upper case. (I am assuming this answer would also assist lower case, string replacements.. etc.)
I've been trying to use UPPER, JSON_ARRAY, JSON_QUOTE, JSON_UNQUOTE, etc - at best I end up with a string representation of what I want.
How can I do this? I'm running MySQL 5.7.19.
You need to use JSON casting. Try the following:
UPDATE users
SET profile = JSON_SET(
profile,
'$.countriesVisited',
CAST(
UPPER(profile->'$.countriesVisited')
AS JSON
)
);

postgres json_populate_recordset not working as expected

I have a table called slices with some simple json objects that looks like this:
id | payload | metric_name
---|---------------------------------------|------------
1 | {"a_percent":99.97,"c_percent":99.97} | metric_c
2 | {"a_percent":98.37,"c_percent":97.93} | metric_c
many records of this. I am trying to get this:
a_percent | c_percent
----------|----------
99.97 | 99.97
98.37 | 97.93
I am creating the type and using json_populate_recordset along with json_agg in the following fashion:
CREATE TYPE c_history AS(
"a_percent" NUMERIC(5, 2),
"c_percent" NUMERIC(5, 2)
);
SELECT * FROM
json_populate_recordset(
NULL :: c_history,
(
SELECT json_agg(payload::json) FROM slices
WHERE metric_name = 'metric_c'
)
);
The clause select json_agg(...) by itself produces a nice array of json objects, as expected:
[{"a_percent":99.97,"c_percent":99.97}, {"a_percent":98.37,"c_percent":97.93}]
But when I run it inside json_populate_recordset, I get Error : ERROR: must call json_populate_recordset on an array of objects.
What am I doing wrong?
This is a variant of #TimBiegeleisen's solution with the function json_populate_record() used in a from clause:
select id, r.*
from slices,
lateral json_populate_record(null::c_history, payload) r;
See rextester or SqlFiddle.
You don't need to use json_agg, since it appears you want to get the set of a_percent and c_percent values for each id in a separate record. Rather just call json_populate_recordset as follows:
SELECT id, (json_populate_record(null::c_history, payload)).* FROM slices