how to fetch songs based on multiple conditions from joined tables - mysql

I have two tables songs and song_clubs. The schema is below:-
songs schema
id available_for song_name status
1 all Song 1 1
2 selection Song 2 1
3 selection Song 3 1
song_clubs schema
song_id club_id
2 1
2 2
3 2
Now i want to fetch the songs of club id 1 and the song is available for all clubs.
My execpted output is like below:-
id available_for song_name
1 all Song 1
2 selection Song 2
I have tried below Query
select id,available_for,song_name from songs
JOIN
song_clubs
on song_clubs.song_id = songs.id
WHERE songs.status =1 and song_clubs.club_id=1 or songs.available_for ='all'
But its only returning one entry that is selection based.

You can do it with EXISTS:
SELECT s.id, s.available_for, s.song_name
FROM songs s
WHERE s.status =1 AND (
s.available_for = 'all'
OR EXISTS (SELECT 1 FROM song_clubs c WHERE c.club_id = 1 AND c.song_id = s.id))
or with the operator IN:
SELECT id, available_for, song_name
FROM songs
WHERE status =1 AND (
available_for = 'all'
OR id IN (SELECT song_id FROM song_clubs WHERE club_id = 1))

Two things.
Use parentheses to group WHERE clauses; otherwise they evaluate left-to-right.
Use LEFT JOIN to avoid losing items from your first table that don't match any items in your second table.
This should work (https://www.db-fiddle.com/f/6dAz91ejhe8AbGECFDihbu/0)
SELECT id,available_for,song_name
FROM songs
LEFT JOIN song_clubs ON songs.id = song_clubs.song_id
WHERE songs.status = 1
AND (song_clubs.club_id=1 or songs.available_for ='all')
ORDER BY id;

you can use this answer too
select unique id,available_for,song from songs,song_clubs
WHERE (song_clubs.song_id = songs.id and songs.status = 1 and song_clubs.club_id=1) or (songs.available_for ='all');
Here I use full join to select all the matches and then select the unique id values for the songs so you can get only the required 2 rows
Note: It is not the best performance query if you have huge tables.
and it is better to use EXISTS or LEFT JOIN.so other answers are more better for performance and this answer is just another way to do that.

Related

How to write SELECT query from 2 different tables based on result from 3rd table, while keeping order?

I have 3 tables where one table has 3 columns with foreign keys to the other two tables.
table album_posters_albums-
+---------+---------+---------+
| album_id|poster_id|albums_id|
+---------+---------+---------+
| 49 | 167 | NULL |
| 49 | NULL | 45 |
+---------+---------+---------+
album_id and albums_id references the album table and poster_id represents the poster table.
I need to
SELECT * FROM poster
WHERE poster_id IN (
SELECT poster_id
FROM album_poster_albums
WHERE album_id=49);
IF the poster_id IS NULL:
SELECT * FROM album
WHERE album_id IN (
SELECT poster_id
FROM album_poster_albums
WHERE album_id=49).
The problem is I need to keep the posters and albums in the same order as they occur in the album_posters_albums table.
I was sending a query to get the list of ids, then looping through each result and querying the db to get either the poster or album but that is obviously very inefficient when I should be able to do it in one query.
It sounds like you want to use INNER JOINS
SELECT album.*, poster.*
FROM album_poster_albums
INNER JOIN album ON album_poster_albums.albums_id = album.album_id
INNER JOIN poster ON album_poster_albums.poster_id = poster.poster_id
WHERE album_poster_albums.album_id = 49
Based on your comment about one row with a poster and one row with an album, UNION ALL might be what you're looking for. (We'd need to see more details about the tables and a few more rows to understand the ordering part.) This should give you an album row then a poster row for each album id.
Caveats: The number and the orders of columns in the album and poster tables must be the same. Also, the data types of those columns must be the same or compatible. (I haven't used a UNION, or UNION ALL, in a very long time.)
SELECT * FROM (
SELECT album.*
FROM album_poster_albums
INNER JOIN album ON album_poster_albums.albums_id = album.album_id
WHERE album_poster_albums.album_id = 49
UNION ALL
SELECT poster.*
FROM album_poster_albums
INNER JOIN poster ON album_poster_albums.poster_id = poster.poster_id
WHERE album_poster_albums.album_id = 49
)
ORDER BY album_id
DECLARE #rowId INT(11);
SET #rowId :=0;
SELECT * FROM(SELECT #rowId:=#rowId+1,t.album_id,album.*
FROM album_poster_albums t
INNER JOIN album ON album.albums_id = t.albums_id
WHERE t.album_id = 49
UNION
SELECT #rowId:=#rowId + 2,t.album_id,poster.*
FROM album_poster_albums s
INNER JOIN poster ON poster.poster_id = t.poster_id
WHERE t.album_id = 49) T
ORDER BY #rowId,t.album_id
I decided to create a new table with an auto increment field based on #beltouche comment. My Mysql is pretty rusty and I thought there may be a way using case or if null. I didn't need the unique id previously with how I wrote the queries.
In hindsight the solution is obvious.
SELECT * FROM (SELECT albums.*, 1 AS type, t.id
FROM album_poster_album t
INNER JOIN albums ON albums.album_id = t.albums_id
WHERE t.album_id = 49
UNION ALL
SELECT poster.*, 2 AS type, s.id
FROM album_poster_album s
INNER JOIN poster ON poster.posterID = s.poster_id
WHERE s.album_id = 49) T
ORDER BY t.id

Find the group id of chat group from user ids that are part of group mysql

I am developing a chat application.
I can find the group id if group contains only two people with the given sql query.
My db structure is (userId and groupId are foreign keys):
Group:
id | name | createdAt
1 foo
2 boo
GroupUser:
userId | groupId | createdAt
1 1
2 1
3 1
select guf.groupId
from GroupUser guf
INNER join GroupUser gu
on guf.groupId = gu.groupId
where guf.userId = 1
and gu.userId != guf.userId
and EXISTS (
select gu.groupId
from GroupUser
where gu.userId = 2
)
Currently i can't find group id of conversation which includes at least 3 or more people. To be more specific for example group contains 4 people. Based on their ids i want to find group id. Like what is the group id that given 4 specific people in it ? It must return for example 1(which is groupId).
If you want groups that contain three people:
select gu.group_id
from groupusers gu
where gu.user_id in (1, 2, 3)
group by gu.group_id
having count(*) = 3;
If you want exactly three people, you can move the condition to the having clause:
select gu.group_id
from groupusers gu
group by gu.group_id
having sum(gu.user_id in (1, 2, 3)) = 3;
As written, these queries assume that groupusers does not have duplicate rows. That seems like a reasonable assumption. The logic could be tweaked to use count(distinct) if duplicates are allowed.

Many-To-Many select only rows with exactly same tags

I have 3 tables: tags, products and relation table between them.
Relation table looks for example like this:
tagId | ProductId
1 | 1
2 | 1
2 | 9
The user can pick two options "All of these" or "One of these".
So if user picks All of these, it's means that the product must have exactly all of tags which the user chose.
So if user pick tags with id 1 and 2, it should select only product with id 1, because this product has exactly the same tags the user chose. (Another way is if the user picks the tag with id 2, it should select only product with id 9.)
So, the product has to have all tags which the user chose (no more, no less).
SQL that I already have for Any/One of these:
SELECT DISTINCT s.SKU
FROM SKUToEAN as s
LEFT JOIN ProductDetails as p ON s.ProductDetailID=p.id
JOIN ProductTagRelation as ptr ON (ptr.productId=p.id and ptr.tagId IN(Ids of selected tags))
Example behavior:
TagId = 1 it should select => None
TagId = 2 it should select => 9
TagId = 1,2 it should select = 1,9
So probably I need two queries. One for any/one of these ( I already have this one ) and the second for all of these.
With PHP I decide which query to use.
You can GROUP BY on the ProductID and use conditional aggregation based filtering inside the Having clause. MySQL automatically casts boolean values to 0/1 when using in numeric context. So, in order to have a specific tagID value available against a ProductID, its SUM(tagId = ..) should be 1.
All of these:
SELECT ptr.productId, s.SKU
FROM SKUToEAN AS s
LEFT JOIN ProductDetails AS p
ON p.id = s.ProductDetailID
JOIN ProductTagRelation AS ptr
ON ptr.productId = p.id
GROUP BY ptr.productId, s.SKU
HAVING SUM(ptr.tagID = 1) AND -- 1 should be there
SUM(ptr.tagID = 2) AND -- 2 should be there
NOT SUM(ptr.tagID NOT IN (1,2)) -- other than 1,2 should not be there
Is this you are looking for (for all condition)?
select product.id
from products
inner join <table> on products.id = <table>.productId
group by product.id
having group_concat(<table>.tagId order by <table>.tagId separator ',') = '1,2';

Join two MySQL Tables and get result from categories

SELECT art.*,arg. FROM rd_articles AS art
LEFT JOIN rd_argument AS arg ON art.cat=arg.id WHERE art.enabled=1 ORDER BY art.id DESC
LIMIT 10
This is simple join query
Article table structure is
ID cat Description Date
1 1 Abc 08-01-2014
2 1 Aaa 10-01-2014
3 2 Abcv 11-01-2014
4 3 Aaa 12-01-2014
5 3 Aaa 14-01-2014
Arguments table is
ID Name
1 A
2 B
3 C
I want pick last updated(Date) one item from each cat.
How ?
This assumes that the enabled column is in rd_articles:
SELECT art.*, arg.*
FROM (
SELECT * FROM rd_articles
INNER JOIN (
SELECT cat, MAX(date) AS maxdate
FROM rd_articles
WHERE enabled = 1
GROUP BY cat
) md ON rd_articles.cat = md.cat AND rd_articles.date = md.maxdate
) art
LEFT JOIN rd_argument AS arg ON art.cat = arg.id
The innermost query gets the maximum date for each category, then joins it to the rd_articles table to get only those rd_articles rows that have the latest date for each article. That becomes the cat alias, which is then left-joined to the arguments table just like in your original query. You can add the LIMIT 10 at the end if needed; I wasn't sure what to do with that.
Note that if there's a tie for a category's latest date, you'll get more than one row for each category. If a tie could happen you'll need to break the tie somehow, for example by using the description or the ID. Let me know if that's the case and I'll update my answer.
SELECT ART.*, ARG.*
FROM ARTICLE AS ART
INNER JOIN RD_AGRUEMENT AS ARG
ON ARG.ID = ART.ID
WHERE (ID, DATE) IN
(SELECT ID, MAX(DATE) FROM ARTICLE GROUP BY ID)

Filter data from two tables

Please help to make the request.
There are two tables, artist and album.
I want to select only those artists whose albums contains a picture.
That is, if the actor has 10 albums and only one of them has a picture or does not contain, i want to skip (only if all albums artist has pictures)
table artist:
artist_id
---------|
1 |
2 |
table albums:
artist_id | album_id | picture_id
---------------------------------
1 | 122... | true
1 | 123... | false
2 | 124... | true
2 | 125... | true
So, I want to select only artist where artist_id=2 (because all the albums have pictures);
there are a lot of solutions for this, the first that comes to my mind is a very simple sub-select to exclude the artists that have an album without an image:
SELECT
*
FROM
artist a
WHERE
artist_id NOT IN
(SELECT DISTINCT artist_id FROM album WHERE picture_id = false);
EDIT:
if you have stored artists without albums, these have to be excluded, too (take a look at the comments for explanation). in that case, you'll have to add something like:
AND
1 <= (SELECT COUNT(*) FROM album WHERE artist_id = a.artist_id)
You can use a GROUP BY clause to find the count of albums by artists. You can then compare this count with the made-up count. Aggregate functions can be used inside HAVING clause to eliminate groups:
SELECT artist_id
FROM albums
GROUP BY artist_id
HAVING COUNT(1) = COUNT(CASE WHEN picture_id THEN 1 ELSE NULL END)
Complete example:
SELECT artist.artist_id, artist.name
FROM artist
INNER JOIN albums ON artist.artist_id = albums.artist_id
GROUP BY artist.artist_id, artist.name
HAVING COUNT(album_id) = COUNT(CASE WHEN picture_id THEN 1 ELSE NULL END)
Another solution, without subselects, using the MIN() operator - if the minimum is TRUE, then all values are true:
SELECT artists.artist_id,MIN(picture_id)
FROM artists
INNER JOIN albums ON artists.artist_id = albums.artist_id
GROUP BY artists.artist_id
HAVING MIN(IFNULL(picture_id, 0)) > 0
EDIT
Modified the query to substitute the null values with 0 at the MIN() opertor, it's cleaner this way.
$sql = mysql_query("SELECT DISTINCT table1.id, table2.id, table2.name
FROM table1, table2 WHERE id=id GROUP BY name");
Try this. Sure it works.
Cheers !!!