selecting two rows that match a Foreign id? - mysql

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')

Related

Very basic Beginner Join Query for 3 tables

I have 3 tables, 'film', 'actor' and 'cast'. I want to get a list of actors that are in a certain film, but I am brand new to SQL and cannot figure out how to join the tables.
I am comfortable with the SELECT and WHERE, just not the FROM.
Film has the following fields: id, title and year.
Actor has the fields id and name.
Cast has the fields filmid and actorid
You need to join the tables like this
select a.name
from actor a
join cast c on a.id = c.actorid
join film f on f.id = c.filmid
where f.title = 'your movie title'

Left Join not returning all rows in left table

I have a film database, one of the tables is 'categories' which holds all of the genres a movie in the database could have.
Running the following query:
SELECT category.name FROM category
Gives back the rows:
Action
Animation
Children
Classics
Comedy
Documentary
Drama
Family
Foreign
Games
Horror
Music
New
Sci-Fi
Sports
Travel
I am trying to query the database to find the # of each particular genre a specific actor has a role in, but I want it to return ALL of the genres with the # in the next column. This was my initial query:
SELECT q1.name AS 'Film Category', COUNT(q2.name) AS '# of Films Ed Chase has appeared in'
FROM (
SELECT category.name FROM category
) AS q1
LEFT JOIN (
SELECT category.name FROM category
INNER JOIN film_category ON film_category.category_id = category.category_id
INNER JOIN film ON film.film_id = film_category.film_id
INNER JOIN film_actor ON film.film_id = film_actor.film_id
INNER JOIN actor ON actor.actor_id = film_actor.actor_id
WHERE actor.first_name = 'ED' AND actor.last_name = 'CHASE'
) AS q2
ON q1.name = q2.name AND '# of Films Ed Chase has starred in' >= 0
GROUP BY q2.name
ORDER BY q2.name;
And it gives back this table, missing some of the rows (I want it to give all the genres, not just the one's that the actor had a role in):
Film Category # of Films Ed Chase has appeared in
Animation 0
Action 2
Classics 2
Documentary 6
Drama 3
Foreign 2
Music 1
New 2
Sci-Fi 1
Sports 2
Travel 1
Interestingly it returned '0' for 'Animation' but no rows for 'Children' or 'Comedy' which is the result I was looking for (all genres returned). What am I doing wrong?
COUNT() doesn't count NULLs, and your left join puts NULLs in the records where there isn't a value in the right-hand table. Your outer join actually is (or should be - check this) returning all the desired rows, but the outer select with the aggregate function is eating them.
Try moving your count() into the right-hand subquery, and then in your outer query replace it with IFNULL(q2.count, '0') to replace NULLs with zeros.
I will note that you should be grouping on q1.name, not q2.name.

How to do a many to many subquery in 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;

MYSQL, Max,Group by and Max

I am having the following two table.
1.Movie Detail (Movie-ID,Movie_Name,Rating,Votes,Year)
2.Movie Genre (Movie-ID,Genre)
I am using the following query to perform join and get the movie with highest rating in each
genre.
select Movie_Name,
max(Rating) as Rating,
Genre from movie_test
inner join movie_genre
where movie_test.Movie_ID = movie_genre.Movie_ID
group by Genre
In the output Rating and Genre are correct but the Movie_Name is incorrect.
can anyone suggest what changes I should make to get the correct movie name along with rating and genre.
SELECT g.*, d.*
FROM MovieGenre g
INNER JOIN MovieDetail d
ON g.MovieID = d.MovieID
INNER JOIN
(
SELECT a.Genre, MAX(b.Rating) maxRating
FROM MovieGenre a
INNER JOIN MovieDetail b
ON a.MovieID = b.MovieID
GROUP BY a.Genre
) sub ON g.Genre = sub.Genre AND
d.rating = sub.maxRating
There is something wrong with your schema design. If a Movie can have many Genre as well as Genre can be contain on many Movie, it should be a three table design.
MovieDetails Table
MovieID (PK)
MovieName
MovieRating
Genre Table
GenreID (PK)
GenreName
Movie_Genre Table
MovieID (FK) -- compound primary key with GenreID
GenreID (FK)
This is a common MySQL problem - specifying non-aggregate/non-aggregated-by columns in an aggregate query. Other flavours of SQL do not let you do this and will warn you.
When you do a query like yours, you are selecting non-aggregate columns in an aggregated group. Since many rows share the same genre, when you select Movie_Name it picks one row at random from each group and displays that one, because there is no general algorithm to guess the row you want and return the values of that.
You might ask 'why does it pick randomly? It could pick the one that max(Rating) belongs to?' but what about other aggregate columns, like avg(Rating)? What row does it pick there? What if two rows have the same max, anyway? Therefore it cannot have an algorithm to pick a row.
To solve a problem like this, you have to restructure your query, something like:
select Movie_Name,
Rating,
Genre from movie_test mt
inner join movie_genre
where movie_test.Movie_ID = movie_genre.Movie_ID
and Rating = (select max(Rating) from movie_test mt2 where mt.Genre = mt2.Genre
group by Genre
limit 1
This will select the row with the rating being the same as the maximum rating for that genre, using a subquery.
Query:
SELECT t.Movie_Name,
t.Rating,
g.Genre
FROM movie_test t
INNER JOIN movie_genre g ON t.Movie_ID = g.Movie_ID
WHERE t.Movie_ID = (SELECT t1.Movie_ID
FROM movie_test t1
INNER JOIN movie_genre g1 ON t1.Movie_ID = g1.Movie_ID
WHERE g1.Genre = g.Genre
ORDER BY t1.Rating DESC
LIMIT 1)

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.