Removing an element in an MySQL's JSON array in a column - mysql

I have a "table" named users with a column name "group_in" which stores an array like [1,2,3].
I am trying to remove an element with a value (eg. 1) for a specified row (eg id:1).
Before:
id| group_in
1 | **[1,2,3]**
2 | [1,3]
After:
id | group_in
1 | **[2,3]**
2 | [1,3]
I have tried the following:-
Update users
SET group_in = JSON_REMOVE(group_in,JSON_UNQUOTE(JSON_search(group_in, 'one', 1)))
where id = 1
but I got back is
id | group_in
1 | null
2 | [1,3]
Screenshot of my table and query result for your reference.
My table
Result gotten
Please help me if you know how to solve it
Thank you 🙏

I believe JSON_SEARCH works on strings, not sure, if it was extended to integer searches as well.
One way is to flatten the array and then recombine it while excluding the value as needed.
Query -
update users set group_in = (select new_grp from (
select id,json_arrayagg(grp) new_grp from users,
JSON_TABLE(group_in, "$[*]" COLUMNS(grp INT PATH '$')) as grp_id
where id=1
and grp<>1
group by id
)X
)
where id=1;
Refer fiddle here.

Try this,
UPDATE `channels` SET `group_in` = JSON_REMOVE(`group_in`, '$[2]') WHERE `id` = 1 ;

Related

Query mysql json column array using AND NOT CONTAINS (mysql 5.7)

I have a table with a json column that contains an array of objects, like the following:
create table test_json (json_id int not null primary key, json_data json not null) select 1 as json_id, '[{"category":"circle"},{"category":"square", "qualifier":"def"}]' as json_data union select 2 as json_id, '[{"category":"triangle", "qualifier":"xyz"},{"category":"square"}]' as json_data;
+---------+----------------------------------------------------------------------------------------+
| json_id | json_data |
+--------------------------------------------------------------------------------------------------+
| 1 | [{"category":"circle"}, {"category":"square", "qualifier":"def"}] |
| 2 | [{"category":"triangle", "qualifier":"xyz"}, {"category":"square"}] |
+---------+----------------------------------------------------------------------------------------+
I'd like to be able to query this table to look for any rows (json_id's) that contain a json object in the array with both a "category" value of "square" and no "qualifier" property.
The sample table above is just a sample and I'm looking for a query that would work over hundreds of rows and hundreds of objects in the json array.
In MySQL 8.0, you would use JSON_TABLE() for this:
mysql> select json_id, j.* from test_json, json_table(json_data, '$[*]' columns (
category varchar(20) path '$.category',
qualifier varchar(10) path '$.qualifier')) as j
where j.category = 'square' and j.qualifier is null;
+---------+----------+-----------+
| json_id | category | qualifier |
+---------+----------+-----------+
| 2 | square | NULL |
+---------+----------+-----------+
It's not clear why you would use JSON for this at all. It would be better to store the data in the normal manner, one row per object, with category and qualifier as individual columns.
A query against normal columns is a lot simpler to write, and you can optimize the query easily with an index:
select * from mytable where category = 'square' and qualifier is null;
I found another solution using only MySQL 5.7 JSON functions:
select json_id, json_data from test_json
where json_extract(json_data,
concat(
trim(trailing '.category' from
json_unquote(json_search(json_data, 'one', 'square'))
),
'.qualifier')
) is null
This assumes the value 'square' only occurs as a value for a "category" field. This is true in your simple example, but I don't know if it will be true in your real data.
Result:
+---------+------------------------------------------------------------------------+
| json_id | json_data |
+---------+------------------------------------------------------------------------+
| 2 | [{"category": "triangle", "qualifier": "xyz"}, {"category": "square"}] |
+---------+------------------------------------------------------------------------+
I still think that it's a CodeSmell anytime you reference JSON columns in a condition in the WHERE clause. I understood your comment that this is a simplified example, but regardless of the JSON structure, if you need to do search conditions, your queries will be far easier to develop if your data is stored in conventional columns in normalized tables.
Your request is not clear. Both of your SQL records has not such properties but your JSON object has. Maybe you try to find any record that has such object. So the following is your answer:
create table test_json (json_id int not null primary key, json_data json not null) select 1 as json_id, '[{"category":"circle", "qualifier":"abc"},{"category":"square", "qualifier":"def"}]' as json_data union select 2 as json_id, '[{"category":"triangle", "qualifier":"xyz"},{"category":"square"}]' as json_data;
select * from test_json;
select * from test_json where 'square' in (JSON_EXTRACT(json_data, '$[0].category'),JSON_EXTRACT(json_data, '$[1].category'))
AND (JSON_EXTRACT(json_data, '$[0].qualifier') is NULL || JSON_EXTRACT(json_data, '$[1].qualifier') is NULL);
See Online Demo
Also see JSON Function Reference

MySQL: get rows with value in array of strings [duplicate]

I'm still pretty new to handling JSON fields in MySQL. All the solutions I've come across deal with objects that have key/values; unable to find one that handles JSON arrays.
Anyways, what I want to do is to be able to select all rows where the interestIds contain 2 in them. How do I do that? Thanks.
Users table
+----+-------------+
| id | interestIds |
+----+-------------+
| 1 | [1, 2] |
| 2 | [3, 2] |
| 3 | [2, 4] |
+----+-------------+
Sample test query:
SET #userId = 2;
SELECT * FROM Users
WHERE #userId IN JSON_CONTAINS(#user, interestIds, '$[1]');
I am confused as how to use the JSON_* functions; not sure what to put for the 3rd parameter...
You can use the following solution, using JSON_CONTAINS:
SELECT *
FROM Users
WHERE JSON_CONTAINS(interestIds, '2') = 1;
The third (optional) paramater path gives you the posibility to use this function only on a specific part of your JSON value. So the following example checks if 2 is the second value of the array:
SELECT *
FROM test
WHERE JSON_CONTAINS(interestIds, '2', '$[1]') = 1;
demo on dbfiddle.uk
Use JSON_SEARCH which returns path to element you are searching, or null if not found:
SELECT *
FROM users
WHERE JSON_SEARCH(interestids, 'one', '2') IS NOT NULL
Live Demo
If you're storing many-to-many relationship using simple JSON array, there are better ways to do it. Consider creating user_interest table and doing it the right and simpler way. That is if your JSON actually looks like you have shown us and does not contain dynamic key-value pairs.
SQL> select id
from users
where JSON_CONTAINS(interestIds, "2","$");
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.0015 sec)
Wrap your select to JSON_ARRAYAGG
Like:
SELECT JSON_ARRAYAGG(JSON_OBJECT(....)) FROM table....

MySQL SELECT order by values of 2 columns

I have a table like this:
CREATE TABLE rows(
UniqueID VARCHAR(225),
Previous VARCHAR(225),
Next VARCHAR(225)
);
With content, that looks like this:
+----------+-----------+-----------+
| UniqueID | Previous | Next |
+----------+-----------+-----------+
| 676 | undefined | 219 |
| 890 | 219 | undefined |
| 219 | 676 | 890 |
+----------+-----------+-----------+
As you can see, the rows have UID's, which the Previous and Next columns refer to.
What I now want, is to write a SELECT * statement, that would order all the results, by the Previous and Next fields. The undefined values mark the end elements. How could I achieve that? In the case of the table showed above, the order I'd want is what's shown there, with the last 2 row positions swapped, so Next of row X Points to a UID of row Y, that has a Previous that points to the UID of the row X. etc.
What you're trying to create is a recursive query. Unfortunately, MySQL does not make this easy. There are relatively simple solutions if the parents always have an index greater than the children, but that is not the case here. There are several questions discussing this type of problem. The following question has answers that explore the different ways to attempt this type of query including using stored procedures.
How to do the Recursive SELECT query in MySQL?
Going with the stored procedure idea, you could try something like:
CREATE PROCEDURE getInOrder()
BEGIN
DECLARE child_id VARCHAR(256);
DECLARE prev_id VARCHAR(256);
SELECT UniqueID INTO prev_id FROM rows WHERE Previous = 'undefined';
SELECT `Next` INTO child_id
FROM rows WHERE UniqueID = prev_id;
CREATE TEMPORARY TABLE IF NOT EXISTS temp_table AS (SELECT * FROM rows WHERE 1=0);
TRUNCATE TABLE temp_table;
WHILE child_id <> 'undefined' DO
INSERT INTO temp_table SELECT * FROM rows WHERE UniqueID = prev_id;
SET prev_id = child_id;
SELECT `Next` INTO child_id
FROM rows WHERE UniqueID = prev_id;
END WHILE;
INSERT INTO temp_table SELECT * FROM rows WHERE UniqueID = prev_id;
SELECT * FROM temp_table;
END;
You can then call the stored procedure to retrieve the table in order.
Working example: http://sqlfiddle.com/#!9/085dec/2
ORDER BY IFNULL(prev, ''), -- some value lower than the rest
IFNULL(next, 'zzzzz') -- some value higher than all values
(Technically, the first part could be simply prev, without the IFNULL.)
If the ids are really numbers, you should use a numeric datatype such as INT UNSIGNED. If they are really strings, do you need 225?
This assumes that prev < next -- Is that necessarily the case? It seems like arbitrary links might not maintain that. If you need to look at next to load the next row based on UniqueId, the code is much more complex.
I think this request lacks on details.
But, you want the final result to be like this?
+----------+-----------+-----------+
| UniqueID | Previous | Next |
+----------+-----------+-----------+
| 676 | undefined | 219 |
| 219 | 676 | 890 |
| 890 | 219 | undefined |
+----------+-----------+-----------+
If I'm right, you can achieve it with (I named the table as demo):
SELECT d.* FROM (
SELECT UniqueID, IF(Previous IS NULL, -1, Previous) AS Previous, IF(Next IS NULL, 999999999999, Next) as Next
FROM demo
)t
JOIN demo d ON d.UniqueID = t.UniqueID
ORDER BY t.Next, t.Previous
;
So, when Previous is NULL you put it with -1 to ensure he's is the first on the list and when Next is NULL you put it with a very high value to ensure it will be the last on the list... then you just have to order the query by Previous and Next.
I must stress that this solution is focused on presented data.

can I do this with pure SQL?

Let's say I have a table like this:
name |order_id
=======================
first_record | 0
second_record | 0
third_record | 0
[...]
I want to update just order_id with an incremented value (not sure how to put this correctly in english - feel free to edit with a better description). See expected output below:
name |order_id
=======================
first_record | 1
second_record | 2
third_record | 3
[...] | n
I know how to do this either by using a script in some programming language or a sql procedure, both solution involve looping the whole table.
You can update table:
SET #oo = 0;
UPDATE table SET `order_id`=#oo:=#oo+1 ORDER By something;
Or just get order_id as returned row number while selecting:
SET #oo = 0;
SELECT name, #oo:=#oo+1 order_id FROM table ORDER By something;
Do you mean something like this? The question is not that clear.
DECLARE #order_id int
SET #order_id = 0
UPDATE #tmp_Users
SET #order_id = order_id = #order_id + 1

MySQL : interval around id column and return another one from subquery with multiple columns

I would like to run a query from a table where the content is like that :
id | col1 | col2 | col3
-----------------------
1 | i_11 | i_12 | i_13
2 | i_21 | i_22 | i_23
3 | i_31 | i_32 | i_33
.. | ... | ... | ...
SELECT col1 FROM table WHERE id IN
(SELECT id-1, id+1 FROM table WHERE col1='xxx' AND col2='yyy' AND col3='zzz')
The aim is to get an interval [id-1, id+1] based on the id column which returns the content stored in col1 for id-1 and id+1. The subquery works but I guess I have a problem with the query itself, since I'm having an error "Operand should contain only one column". I understand it, but I don't see any other way to do it in one query ?
I'm quite sure there's a pretty easy solution but I can't figure it out for the moment, even after having carefully read other posts about multiples columns' subqueries...
Thank you for any help :-)
The only way I can think to do it right now is like this:
SELECT col1
FROM table T
WHERE id BETWEEN (SELECT id FROM table WHERE col1='xxx' AND col2='yyy' AND col3='zzz') -1
and (SELECT id FROM table WHERE col1='xxx' AND col2='yyy' AND col3='zzz') +1
Your problem is that you are retrieving two values - but as a list rather than a set. The SQL optimizer can't see 1,3 as a set of two items when they are presented in a single row. There may also be a cast needed.
This should work.
SELECT col1 FROM table WHERE id in
(
select cast(id as int) -1 from table where col1='i_21'
union
select cast(id as int) +1 from table where col1='i_21'
)