MySQL Convert JSON column to array and get pages - mysql

It's pretty difficult to pick a title, I'm sorry for that!
What I have for a database structure is the following columns:
pages
id (int)
url (string)
content (string)
created_at (timestamp)
groups
id (int)
related_page_ids (json)
domain_id (int)
created_at (timestamp)
What I want to achieve is to retrieve all groups by a selected domain ID and then retrieve all the related_pages with it. If I should write it in ugly PHP and MySQL:
$groups = SELECT * FROM groups WHERE domain_id = 1;
foreach($groups as $group){
$pages = SELECT * FROM pages WHERE IN id = implode($group['related_page_ids']);
}
I hope that you understand my goals with the ugly example.

If you have access to JSON_TABLE (MySQL 8 / MariaDB 10.6) it can be done with that in a single query:
SELECT * FROM pages
WHERE id IN (
SELECT * FROM JSON_TABLE(
(SELECT JSON_ARRAYAGG(related_page_ids) FROM groups WHERE domain_id=1),
'$[*][*]' columns(rel_id INTEGER path '$')) AS jt );
The column needs to actually be of type JSON (not varchar) for this to work.

Related

Querying JSON column in MySQL with id

I have a table in MySQL with data in a column in the following format:
[{"type_id":1,"price":50},{"type_id":3,"price":60}]
I need to find out price of the item based on its id. For example, I need to find out price of an item with type_id = 3
I have tried:
select JSON_EXTRACT(JSONColumn, '$[*].price') as prices, JSON_EXTRACT(JSONColumn, '$[*].type_id') as quantities from Items where ItemId = 123
and json_contains(JSONColumn, '{"type_id" : 3}')
This is not working. Can someone specify the correct way of querying json data?
SELECT test.id, jsontable.price
FROM test
CROSS JOIN JSON_TABLE (test.val,
'$[*]' COLUMNS (type_id INT PATH '$.type_id',
price INT PATH '$.price')) jsontable
WHERE jsontable.type_id = 3;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=baa0a24a4bbf10ba30202c7156720018

mySQL WHERE clause with column = val1 OR coumn IN values

I am trying to query through my db to receive all posts which contain a certain user_id as owner or participant of the post.
My DB structure is as following:
id: INT
media_id: INT
owner_id: INT
participants: STRING eg. [1,2]
comments: TEXT eg. [{},{}]
Here are two example entries:
id: 1, media_id: 2, owner_id: 1, participants: "[1,2]", comments: "[]"
id: 2, media_id: 3, owner_id: 2, participants: "[2,1]", comments: "[]"
What I am trying to achieve is to get all rows where the user with the id 1 is part of the column owner_id OR participants
My current query looks as following:
SELECT * FROM posts WHERE owner_id = 1 OR participants IN "1"
And the result which I receive is only the line where 1 is the owner_id
I might understand the IN equasion wrong but 1 is a part of participants in both rows, therefore I should get both rows as result, shouldn't I?
This will do the trick:
SELECT * FROM posts WHERE owner_id = 1
OR participants like "%,1]"
OR participants like "[1,%"
OR participants like "%,1,%"
If you are willing to change your data structure for participants field to a comma separated value e.g. 1,2 or 2,1, you can then use:
SELECT * FROM posts WHERE owner_id = 1 OR FIND_IN_SET(1, participants);
You should change query to :
SELECT * FROM posts
WHERE (owner_id = 1 OR participants like "%,1" OR participants like "1,%" OR participants like "%,1,%")
You can use the KeyWord Like... the syntax example:
SELECT * FROM posts WHERE owner_id = 1 OR participants Like '%1%'
For other case you can use this:
SELECT * FROM posts WHERE owner_id = 1
OR participants like "%,1"
OR participants like "1,%"
OR participants like "%,1,%"
It is indeed not the correct approach of the IN clause. It should look like:
SELECT * FROM posts WHERE owner_id = 1 OR participants IN (1)
And its only need if looking for multiple values (in this case id's) like:
SELECT * FROM posts WHERE owner_id = 1 OR participants IN (1,5,15)
But because your participants column is a string it is not going to work, because MySQL is not parse/do something with the string to make it usable in the way you tried.
There is a possible and ugly way to achief what you want and do not change your database structure, but I do recommend to change it to a more usable approach with a relation table for a many-to-many relationship?
But the ugly way to achief it:
SELECT * FROM posts
WHERE owner_id = 1
OR (participants LIKE '[1,%' OR participants LIKE '%,1,%' OR participants LIKE '%,1]')
Better approach
A nice way good be to change your posts table slightly:
[posts]
id INT
media_id INT
owner_id INT
comments: TEXT eg. [{},{}]
And add a relational table what could like:
[post_participant]
post_id: INT
user_id: INT // -> or named participant_id
In this way you can get the proper posts with a query like:
SELECT p.* FROM posts p
LEFT JOIN post_participant pp ON pp.post_id = p.id AND pp.user_id = p.owner_id
WHERE p.owner_id = 1
OR pp.user_id IS NOT NULL

Return default value if no results found in SQL query

I currently am using this query to select "profile_picture from the column relation and table media, although I noticed my return default.png inside my html image tag doesn't work for results.
Is there any way I could return:
IF error
"SELECT * FROM media WHERE post_id = '5552773e65a2b' AND relation = 'profile_picture"
My current query
"SELECT * FROM media WHERE userID = $friend->user_id AND relation = 'profile_picture' ORDER BY id DESC LIMIT 1"
While this would probably be easier to handle with your scripting language, you could do a union all to get the default value since you're ordering by the id field:
SELECT id, picture
FROM media
WHERE userID = $friend->user_id
AND relation = 'profile_picture'
UNION ALL
SELECT -1 id, 'default.png' picture
ORDER BY id DESC LIMIT 1
SQL Fiddle Demo

How to loop through each row in MySQL table and get rows if conditions match

For my application I would like to do the following:
SELECT *
FROM `LLS_USERS`
LIMIT 0,111
WHERE
(`USR_LOGIN`=
(CONCAT(
(SELECT LEFT(`USR_FIRST_NAME`, 1) FROM `LLS_USERS`;),
(SELECT `USR_LAST_NAME`FROM `LLS_USERS`;)
);
)
)
Basically I need to select all the rows in the user table where the user's login matches up with the first initial of the user name concatinated with the user's last name. So the SQL query will generate a table for me of all the selected rows where this is true in phpMyAdmin.
I know I have 111 users in my database currently.
Does anyone know what's wrong with my syntax?
UPDATE: SOLUTION is:
SELECT *
FROM `LLS_USERS`
WHERE (
`USR_LOGIN` = ( left( `USR_FIRST_NAME` , 1 ) || `USR_LAST_NAME` )
)
LIMIT 0 , 111;
Take SQL as a (real life) Language here in this basic example:
Give me all rows of a table that match the condition loginname= 1 char of first name following lastname
->
SELECT * FROM LLS_USERS where USR_LOGIN= LEFT(USR_FIRST_NAME, 1)||USR_LAST_NAME
Thats all, thats the beautiness of SQL

In SQL if where condition does not yield result fetch other rows

I have Query like this
Select * from customers where id = 123 and name like '%tester%';
If id : 123 and name : "tester" doesn’t exist in table i should fetch other rows with name "tester" discarding condition "id". if it exists fetch row for that id and name.
Guys i know this is can be handled in program, i want this to be done in my Query, can you please STOP DOWN VOTING and give me the solution if you know!!!
You can try something like this:
SELECT *
FROM CUSTOMERS
WHERE ( ID = 123
AND NAME LIKE '%tester%' )
OR ( NAME LIKE '%tester%'
AND NOT EXISTS (SELECT *
FROM CUSTOMERS
WHERE ID = 123
AND NAME LIKE '%tester%') )
You can find a working example on SQL Fiddle.
SELECT * FROM customers WHERE (id = 123 AND name like '%tester%') OR (name LIKE '%tester%') LIMIT 1;
It should work then
I gotcha here, check it
Select * from customers where id = 3 or name like '%tester%'
order by id=3 desc limit 1;
See you will get all rows that have either id= 3 or name is like tester. From there you will order then by the boolean value (1 if true) by if they == 3. Limiting this to 1 result will get you only the best response.
Likewise if you want to get all results you could remove the limit, assume the top one is the best result. If the first results id is not = 3 then you could say that all the results are just best matches.