How to do a many to many subquery in MySql - mysql

I am working with MySql 5.1 and am building my first many-to-many database. I understand the concepts, I have 3 tables:
Albums, with a unique ID
Genres, with a unique ID
album_genres with columns for each of the ID's from Albums and Genres.
The issue I am having is that, of course, 1 album can have multiple genres. But when I do a search, I'm really in the dark about how to structure it so during searches, I get all the genres for each individual album. Please note, this is not the loading of 1 album, but doing a search that will net 1 or more albums.
Sorry I don't really have anything to show what I've tried because I'm not even sure where to begin.
I'm sure it's easy enough. But all the tutorials I could find only address the basics of M2M but not how to get multiple matching entries.
After looking at many great suggestions, I have built this query:
SELECT
album.album_title,
Concat(genre.genre_id, ',') as GenreName,
count(album.album_id) as GenreCount
FROM $this->album_tbl album
JOIN wp_musicmgr_albgnr albgnr ON albgnr.albgnr_album_fk = album.album_id
JOIN $this->genre_tbl genre ON genre.genre_id = albgnr.albgnr_genre_fk
GROUP BY album.album_id
Which is producing this:
[0] => stdClass Object
(
[album_title] => album q
[GenreName] => 1,
[GenreCount] => 3
)
However, as you can see. Despite having a count of 3 hits on genres, it is listing the first one.

If I got the question you need output like
AlbumName | Genre1,Genre2.........
FOr this you need to use GroupBy
SELECT A.album_name as AlbumName, GROUP_CONCAT(G.genre_name) as GenreName, count(A.ID) as GenreCount
FROM Album A
JOIN album_genres AG
ON (A.ID = AG.album_ID)
JOIN Genre G
ON (G.ID = AG.genre_ID)
Group by A.ID

Join on the two tables.
SELECT cols FROM Albums
JOIN album_genres USING (albumID)
JOIN Genres USING (genreID)
WHERE albumName LIKE :search

Select * from Albums a, Genres g, album_genres ag
where a.unique_id = ag.a_unique_id and g.unique_id = ag.g_unique_id and a.name like '%album-name%'
album-name is the value of album in table album
Also in place of star(*) in Select *, please do put columns name's from Gener table.

To get all Genres of an Album the query would be
SELECT A.album_name, G.genre_name FROM Album A
JOIN album_genres R
ON (A.ID = R.album_ID)
JOIN Genre G
ON (G.ID = R.genre_ID)
WHERE A.ID = '5'
Where 5 would be the album unique id. The basic concept is that you should join the 3 tables, and use JOIN - ON to match the ids of each columns in the relationship table to the entity table one
EDIT: If you want a list of every album with every genre like this one:
Album - Genre
Al01 - Rock
Al02 - Rock
Al01 - Rock
Al01 - Metal
Al03 - Metal
just remove the WHERE A.ID = '5'.

Updated (New Answer):
Note: Tested.
select a.name, group_concat(g.name separator ', ')
from albums a
inner join album_genre ag on a.id = ag.albumid
inner join genres g on ag.genreid = g.id
group by a.id;
Note: GROUP_CONCAT has a maximum length limit. Default limit is 1024, but can changed by modifying group_concat_max_len. Read more: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_group-concat
Old answer:
Search for Albums:
SELECT a.album_name, a.AlbumId FROM `Albums` a
INNER JOIN `album_genres` ag on a.AlbumId = ag.AlbumId
INNER JOIN `Genres` g on ag.GenreId = g.GenreId
WHERE
a.album_name = 'Michael Learns To Dance' AND
g.genre_name = 'ROCK'
GROUP BY a.AlbumId;
Foreach row... perform another search query:
SELECT g.genre_name FROM `Genres` g
INNER JOIN `album_genres` ag on g.GenreId = ag.GenreId
WHERE ag.AlbumId = 1;
....[continue for next row]....
....
WHERE ag.AlbumId = 2;
WHERE ag.AlbumId = 3;
WHERE ag.AlbumId = 4;
......
WHERE ag.AlbumId = 1000;

Related

Get data from a join table in inner join

I have the following four tables:
Subscription:
book_id, duration, subscriber
Book:
book_id, book_name
Genre:
genre_id, genre_name
book_to_genre:
genre_id, book_id
For a certain user (subscriber) I would like to get all rows from subscription. The book name should be fetched from table Book. I know this is done with an inner join:
SELECT Book.book_name, Subscription.duration from Subscription INNER JOIN Book on Subscription.book_id = Book.book_id where Subscription.subscriber = "somevalue";
What if I would like to fetch genre_name from table Genre, where Genre.genre_id = book_to_genre.genre_id for that book?
Here is a modified version of your initial query that will return genre_name:
- I added the field genre_name in the SELECT part
- I added table aliases to make it easier to read
- I added 2 INNER JOIN: one between tables book and book_to_genre, the other between tables book_to_genre and Genre
SELECT b.book_name, s.duration, g.genre_name
FROM Subscription s
INNER JOIN Book b on s.book_id = b.book_id
INNER JOIN book_to_genre bg ON b.book_id = bg.book_id
INNER JOIN Genre g ON bg.genre_id = g.genre_id
where s.subscriber = "somevalue";
Documentation:
- SELECT
- JOIN

selecting two rows that match a Foreign id?

Im trying to get all the movies_name that belong in two genres(categroies),
i.e. SCHEMA
movie_id genre
4 Action
4 Comedy
SQL:
SELECT movies.movie_name
FROM movies
INNER JOIN tags
ON movies.movie_id = tags.movie_id
WHERE tags.genre = 'Comedy'
AND tags.genre = 'Action'
this should bring me back the movie_name of movie_id 4.
this brings me back zero results, when i know thier should be three results using my test data, am i doing the query wrong.
SELECT movies.movie_name
FROM movies
INNER JOIN tags
ON movies.movie_id = tags.movie_id
WHERE tags.genre IN ('Comedy','Action')
GROUP BY movies.movie_name
HAVING COUNT(*) = 2
if unique constraint was not specified on genre for each movie then you need to add DISTINCT
SELECT movies.movie_name
FROM movies
INNER JOIN tags
ON movies.movie_id = tags.movie_id
WHERE tags.genre IN ('Comedy','Action')
GROUP BY movies.movie_name
HAVING COUNT(DISTINCT tags.genre) = 2
SQLFiddle Demo (example data is different but still have same thought)
Tags.genre cannot be BOTH 'Comedy' and 'Action' at the same time. You need an IN clause like such:
`SELECT fields FROM tables WHERE tags.genre IN ('Comedy', 'Action')

select all books from one category except books which belong to other category

I have 3 tables: books, book_categories, categories.
book_categories table "joins" books and categories. It contains columns: id,book_id,category_id.
So one Book may belong to many categories and one Categorie may have many books.
I need query which retrieves all books from given_category except books which belongs to given_set_of_categories. So for example I want all books from category A but only if they don't belong also to category B or C. I need also sort (order) the result by Book.inserted column.
I know how to get all books from given_category with 2 joins but can't figure out how to exclude some books from other categories in result. I cant filter books in PHP because I am paginating the search result.
where
category_id = <given category>
and books.book_id not in
(
select book_id from book_categories
where category_id in (<given set of cat>)
)
order by books.inserted
So, if you mean it is in one category but not in any other:
AND EXISTS(SELECT * FROM books b JOIN book_categories bc ON b.id = bc.book_id JOIN categories c ON bc.category_id = c.id AND c.id = 'A')
AND NOT EXISTS(SELECT * FROM books b JOIN book_categories bc ON b.id = bc.book_id JOIN categories c ON bc.category_id = c.id AND c.id != 'A')
I think that this can be achieved through counting provided that book_categories entries are unique, thus the combination book_id & category_id are not repeating. Instead of trying directly to exclude records, we select from the combined set of categories [,] and then we'll count book_id entries that belong to the :
COUNT(IF(category_id = <given_category>, 1, NULL)) as cnt_exists
and after ensuring that it contains the required category, we count the total to see if it belongs to any other category as well:
COUNT(*) AS cnt_total
SELECT * FROM books b JOIN (
SELECT book_id,
COUNT(IF(category_id = <given_category>, 1, NULL)) as cnt_exists,
COUNT(*) AS cnt_total FROM book_categories WHERE
category_id IN(<given_category>, <given_set_of_categories>)
) bc ON b.id = bc.book_id AND
cnt_exists = 1 AND cnt_total = 1 ORDER BY b.inserted

Mysql query -- choosing highest rank/ordered subject in table (join)

Scenario:
We have 5 users. (users table)
Each user has up to 10 imgs. (image table)
These 10 images can be ordered 1 – 10. (image table)
Each img can be listed in multiple categories (say there are 5 categories – birds, bees, bunnies, brains, belugas (category table connected to img table via table that stores img_ids and category_ids)
In searching through the categories, say someone chooses bees. The search should find the images in that category that is listed CLOSEST to the #1 img for all users. So if each user has 3 images in the bees category, ordered as numbers 4, 7 & 9, the search should show the 4th as its closest to the number 1.
The results I keep getting are all over the place and almost seems like it is choosing the images via WHEN they were added to the DB.
SELECT i.img_name, i.ordered, a.user_name, c.keyword, c.cat_id
FROM images AS i JOIN artists AS a USING (user_id)
JOIN img_cat_table AS im USING ( img_id )
JOIN catkeys AS c USING (cat_id)
WHERE ( cat_id = 3) // THE BEES ID #
GROUP BY user_id ORDER BY user_name DESC
I'm also not sure if you want to show all of the relevant images in the right order, or only the top one. Assuming that it is the latter situation, you will need to join to a subquery or view that returns the min rank for each user, category:
SELECT i.img_name, i.ordered, a.user_name, c.keyword, c.cat_id
FROM images AS i JOIN artists AS a USING (user_id)
JOIN img_cat_table AS im USING ( img_id )
JOIN catkeys AS c USING (cat_id)
JOIN (
SELECT user_id, min(img_rank) img_rank
FROM images AS i
JOIN artists AS a on i.user_id = a.user_id
JOIN img_cat_table AS im on im.img_id = i.img_id
JOIN catkeys AS c on c.cat_id = i.cat_id
WHERE ( cat_id = 3) ) x on x.user_id = a.user_id and x.img_rank = img_rank
WHERE c.cat_id = 3
I'm not sure what the name of the column that holds the image ranking is. I called it img_rank. Hopefully this will give you the idea
though if you can post the table structure and data, that will be great but Here is what I haved tried
SELECT i.img_name, i.ordered, a.user_name, c.keyword, c.cat_id
from (
select img_name, ordered, img_id, user_id from
images
group by user_id
order by user_img ) as i
JOIN artists AS a USING (user_id)
JOIN img_cat_table AS im USING ( img_id )
JOIN catkeys AS c USING (cat_id)
WHERE ( cat_id = 3) // THE BEES ID #
Try removing DESC from your ORDER BY clause.

Mysql query in drupal database - groupwise maximum with duplicate data

I'm working on a mysql query in a Drupal database that pulls together users and two different cck content types. I know people ask for help with groupwise maximum queries all the time... I've done my best but I need help.
This is what I have so far:
# the artists
SELECT
users.uid,
users.name AS username,
n1.title AS artist_name
FROM users
LEFT JOIN users_roles ur
ON users.uid=ur.uid
INNER JOIN role r
ON ur.rid=r.rid
AND r.name='artist'
LEFT JOIN node n1
ON n1.uid = users.uid
AND n1.type = 'submission'
WHERE users.status = 1
ORDER BY users.name;
This gives me data that looks like:
uid username artist_name
1 foo Joe the Plumber
2 bar Jane Doe
3 baz The Tooth Fairy
Also, I've got this query:
# artwork
SELECT
n.nid,
n.uid,
a.field_order_value
FROM node n
LEFT JOIN content_type_artwork a
ON n.nid = a.nid
WHERE n.type = 'artwork'
ORDER BY n.uid, a.field_order_value;
Which gives me data like this:
nid uid field_order_value
1 1 1
2 1 3
3 1 2
4 2 NULL
5 3 1
6 3 1
Additional relevant info:
nid is the primary key for an Artwork
every Artist has one or more Artworks
valid data for field_order_value is NULL, 1, 2, 3, or 4
field_order_value is not necessarily unique per Artist - an Artist could have 4 Artworks all with field_order_value = 1.
What I want is the row with the minimum field_order_value from my second query joined with the artist information from the first query. In cases where the field_order_value is not valuable information (either because the Artist has used duplicate values among their Artworks or left that field NULL), I would like the row with the minimum nid from the second query.
The Solution
Using divide and conquer as a strategy and mysql views as a technique, and referencing this article about groupwise maximum queries, I solved my problem.
Create the View
# artists and artworks all in one table
CREATE VIEW artists_artwork AS
SELECT
users.uid,
users.name AS artist,
COALESCE(n1.title, 'Not Yet Entered') AS artist_name,
n2.nid,
a.field_image_fid,
COALESCE(a.field_order_value, 1) AS field_order_value
FROM users
LEFT JOIN users_roles ur
ON users.uid=ur.uid
INNER JOIN role r
ON ur.rid=r.rid
AND r.name='artist'
LEFT JOIN node n1
ON n1.uid = users.uid
AND n1.type = 'submission'
LEFT JOIN node n2
ON n2.uid = users.uid
AND n2.type = 'artwork'
LEFT JOIN content_type_artwork a ON n2.nid = a.nid
WHERE users.status = 1;
Query the View
SELECT
a2.uid,
a2.artist,
a2.artist_name,
a2.nid,
a2.field_image_fid,
a2.field_order_value
FROM (
SELECT
uid,
MIN(field_order_value) AS field_order_value
FROM artists_artwork
GROUP BY uid
) a1
JOIN artists_artwork a2
ON a2.nid = (
SELECT
nid
FROM artists_artwork a
WHERE a.uid = a1.uid
AND a.field_order_value = a1.field_order_value
ORDER BY
uid ASC, field_order_value ASC, nid ASC
LIMIT 1
)
ORDER BY artist;
A simple solution to this can be to create views in your database that can then be joined together. This is especially useful if you often want to see the intermediate data in the same way in some other place. While it is possible to mash together the one huge query, I just take the divide and conquer approach sometimes.