I am working on an assignment, and I need to find movies that have been directed by directors that directed more than one movie starring Angelina Jolie. Currently, I have this:
SELECT DISTINCT t.title, n.name
FROM (
SELECT DISTINCT t.id theMovies
FROM name n
INNER JOIN cast_info c
ON (c.person_id = n.id)
INNER JOIN title t
ON (t.id = c.movie_id)
WHERE n.name = 'Jolie, Angelina'
) as newTable
INNER JOIN title t
ON (t.id = theMovies)
INNER JOIN cast_info c
ON (c.movie_id = t.id)
INNER JOIN name n
ON (n.id = c.person_id)
CROSS JOIN role_type
WHERE role = 'director';
What this query currently does is find a list of movies starring Angelina Jolie, and then it lists the directors of those movies. All I need to do now is keep only the rows where the director is present in at least one other row. Any tips?
For reference, here is a diagram of the database I'm using:
http://i.imgur.com/kj8qVgF.png
I'm also rather new to SQL so any suggestions to improve my query would be much appreciated!
I would break this up into several pieces and build up to your final query. If you are new to
SQL, it's good practice to break things into bits and put them back together. With that, I'll restate the goal: find movies that have been directed by directors who have directed a movie with Angelina Jolie.
I would start by getting all movies with Angelina Jolie:
SELECT t.id
FROM name n
JOIN cast_info c ON c.person_id = n.id
JOIN title t ON t.id = c.movie_id
WHERE n.name = 'Jolie, Angelina';
Now, let's get the directors of those movies:
SELECT c.person_id
FROM cast_info c
JOIN title t ON t.id = c.movie_id
JOIN role_type r ON r.id = c.role_id
WHERE r.role = 'director' AND t.id IN(SELECT t.id
FROM name n
JOIN cast_info c ON c.person_id = n.id
JOIN title t ON t.id = c.movie_id
WHERE n.name = 'Jolie, Angelina');
We can modify the above query to group by person_id, having a count(*) greater than one (meaning more than one movie).
SELECT c.person_id
FROM cast_info c
JOIN title t ON t.id = c.movie_id
JOIN role_type r ON r.id = c.role_id
WHERE r.role = 'director' AND t.id IN(SELECT t.id
FROM name n
JOIN cast_info c ON c.person_id = n.id
JOIN title t ON t.id = c.movie_id
WHERE n.name = 'Jolie, Angelina')
GROUP BY person_id
HAVING COUNT(*) > 1;
Now, we need to find movies directed by those directors, and filter so that we don't include movies with Angelina Jolie.
SELECT t.id
FROM title t
JOIN cast_info c ON c.movie_id = t.id
JOIN role_type r ON r.id = c.role_id
WHERE r.role = 'director'
AND c.person_id IN (SELECT c.person_id
FROM cast_info c
JOIN title t ON t.id = c.movie_id
JOIN role_type r ON r.id = c.role_id
WHERE r.role = 'director' AND t.id IN(SELECT t.id
FROM name n
JOIN cast_info c ON c.person_id = n.id
JOIN title t ON t.id = c.movie_id
WHERE n.name = 'Jolie, Angelina')
GROUP BY person_id
HAVING COUNT(*) > 1)
AND t.id NOT IN(SELECT t.id
FROM name n
JOIN cast_info c ON c.person_id = n.id
JOIN title t ON t.id = c.movie_id
WHERE n.name = 'Jolie, Angelina');
I can't test via SQL Fiddle because it is not working at the moment but I will do so as soon as I can. Some stuff might need to be tweaked, but let me know if this helps.
Please try:
SELECT
t.title
, n.name
FROM title t
INNER JOIN cast_info c
ON t.id = c.movie_id
INNER JOIN name n
ON c.person_id = n.id
INNER JOIN role_type r
ON c.person_role_id = r.id
INNER JOIN (
SELECT
c.person_id
, r.id
FROM cast_info c
INNER JOIN role_type r
ON c.person_role_id = r.id
WHERE r.role = 'director'
AND c.movie_id IN (
SELECT DISTINCT
c.movie_id
FROM name n
INNER JOIN cast_info c
ON c.person_id = n.id
WHERE n.name = 'Jolie, Angelina'
)
GROUP BY
c.person_id
, r.id
HAVING COUNT(*) > 1
) d
ON c.person_id = d.person_id
AND r.id = d.id
;
Try a multi-part query "in parts" if it doesn't seem to work, this helps identify where it may be failing
-- 1
SELECT DISTINCT
c.movie_id
FROM name n
INNER JOIN cast_info c
ON c.person_id = n.id
WHERE n.name = 'Jolie, Angelina'
;
-- 2
SELECT
c.person_id
, r.id
FROM cast_info c
INNER JOIN role_type r
ON c.person_role_id = r.id
WHERE r.role = 'director'
GROUP BY
c.person_id
, r.id
HAVING COUNT(*) > 1
;
-- 3
SELECT
c.person_id
, r.id
FROM cast_info c
INNER JOIN role_type r
ON c.person_role_id = r.id
WHERE r.role = 'director'
AND c.movie_id IN (
SELECT DISTINCT
c.movie_id
FROM name n
INNER JOIN cast_info c
ON c.person_id = n.id
WHERE n.name = 'Jolie, Angelina'
)
GROUP BY
c.person_id
, r.id
HAVING COUNT(*) > 1
;
Related
SELECT n.name as actor_name , COUNT(r.movie_id) as movie_count
FROM names as n
INNER JOIN director_mapping as dm
ON n.id = dm.movie_id
INNER JOIN movie as m
ON dm.movie_id = m.id
INNER JOIN ratings as r
ON m.id = r.movie_id
WHERE r.median_rating >= 8
GROUP BY actor_name
ORDER BY movie_count DESC
LIMIT 2;
It gives nothing in result, could anyone please help?
Your join on director_mapping was wrong and the column you joined on too:
SELECT n.name as actor_name , COUNT(r.movie_id) as movie_count
FROM names as n
INNER JOIN role_mapping as rm ON n.id = rm.name_id
INNER JOIN movie as m ON m.id = rm.movie_id
INNER JOIN ratings as r ON m.id = r.movie_id
WHERE r.median_rating >= 8
GROUP BY actor_name
ORDER BY movie_count DESC
LIMIT 2;
List all the actors who made a film during the 1950s and also in the 1980s.
When I try exclusively:
SELECT a.name
FROM movies AS m
JOIN castings AS c
JOIN actors AS a
ON m.id = c.movieid
AND c.actorid = a.id
WHERE (m.yr BETWEEN 1950 AND 1959)
or:
SELECT a.name
FROM movies AS m
JOIN castings AS c
JOIN actors AS a
ON m.id = c.movieid
AND c.actorid = a.id
WHERE (m.yr BETWEEN 1980 AND 1989)
I get results for both separate queries. However I get no rows when I combine these.
SELECT a.name
FROM movies AS m1
JOIN movies AS m2
JOIN castings AS c
JOIN actors AS a
ON m1.id = c.movieid
AND m2.id = c.movieid
AND c.actorid = a.id
AND m1.id < m2.id
WHERE (m1.yr BETWEEN 1950 AND 1959) AND (m2.yr BETWEEN 1980 AND 1989);
How can I find the names I'm looking for?
Starting from your existing query, a straight-forward approach is to use aggregation, and filter with a HAVING clause:
SELECT a.name
FROM movies AS m
JOIN castings AS c ON m.id = c.movieid
JOIN actors AS a ON c.actorid = a.id
GROUP BY a.id, a.name
HAVING
MAX(m.yr BETWEEN 1950 AND 1959) = 1
AND MAX(m.yr BETWEEN 1980 AND 1989) = 1
You can use a subquery in where clause.
SELECT
a.name
FROM
actors AS a
WHERE EXISTS
(SELECT
1
FROM
movies m,
castings c
WHERE c.actorid = a.id
AND m.id = c.movieid
AND (m.yr BETWEEN 1950
AND 1959)
LIMIT 1)
OR EXISTS
(SELECT
1
FROM
movies m,
castings c
WHERE c.actorid = a.id
AND m.id = c.movieid
AND (m.yr BETWEEN 1980
AND 1989)
LIMIT 1)
Or you can use Group By like the following while getting number of movies:
SELECT
a.name,
SUM(IF(m.yr BETWEEN 1950 AND 1959, 1, 0)) AS e1950, -- Number of Movies in 1950s
SUM(IF(m.yr BETWEEN 1980 AND 1989, 1, 0)) AS e1980 -- Number of Movies in 1980s
FROM movies AS m
JOIN castings AS c
JOIN actors AS a
ON m.id = c.movieid
AND c.actorid = a.id
GROUP BY a.name HAVING e1950 > 0 AND e1980 > 0;
i have a such type of relations in db.
I wrote such SQL code to get list of user with their average rating.
SELECT
c.id,l.login,
AVG(f.mark) AS AVGRating
FROM logininfo l
INNER JOIN person p
ON l.id = p.info_id
JOIN candidate c
ON p.id = c.id
JOIN feedback f
GROUP BY c.id
ORDER BY AVGRating ASC;
Problem is that I get same rating for all users.
You missed the join condition for the feedback table:
SELECT
c.id,l.login,
AVG(f.mark) AS AVGRating
FROM logininfo l
JOIN person p
ON l.id = p.info_id
JOIN candidate c
ON p.id = c.id
JOIN feedback f
ON c.id = f.candidat_id -- < -- this one
GROUP BY c.id
ORDER BY AVGRating ASC;
Btw, INNER JOIN and JOIN are the same.
SELECT
c.id,l.login,
AVG(f.mark) AS AVGRating
FROM logininfo l
INNER JOIN person p
ON l.id = p.info_id
JOIN candidate c
ON p.id = c.id
JOIN feedback f
--add a join condition
GROUP BY c.id
ORDER BY AVGRating ASC;
I am working on an assignment that requires me to list the actors and actresses that show up in a movie, and then the actors and actresses that have starred with them in other movies. A diagram of the database is viewable here: http://i.imgur.com/kj8qVgF.png
I have a query for the first part (getting the names of actors and actresses that show up in a certain movie).
SELECT DISTINCT n.name
FROM cast_info c
INNER JOIN name n
ON (n.id = c.person_id)
INNER JOIN title t
ON (c.movie_id = t.id)
CROSS JOIN role_type r
WHERE (t.title = 'The Movie') AND (r.role = 'actress' OR r.role = 'actor')
Could I get some assistance to help me find the actresses and actors that star with them in other movies?
Example:
Actors in a given movie 'The Movie': Bob, Joe, Billy
Actors in a different movie 'Another Movie': Joe, Daniel, Frank
Actors in another different movie 'Third Movie': Billy, Susan, Theodore
It should return Daniel, Frank, Susan, and Theodore, because they starred in at least one movie with one of the actors in the given movie.
According to your example, and your model is 3NF, so you can use trivial IN to solve the problem just like you described in Natural language.
SELECT DISTINCT n.name
FROM cast_info c
INNER JOIN name n
ON (n.id = c.person_id)
INNER JOIN title t
ON (c.movie_id = t.id)
INNER JOIN role_type r
ON (c.role_id = r.id)
WHERE (r.role = 'actress' OR r.role = 'actor') -- find the actresses and actors ...
AND c.movie_id IN -- starred in at least one movie ...
(SELECT movie_id
FROM cast_info
WHERE id in -- with one of the actors in the given movie => Bob, Joe, Billy
(SELECT cc.id
FROM cast_info cc
INNER JOIN role_type rr
ON (cc.role_id = rr.id)
WHERE ( rr.role = 'actor') -- only select actors in the given movie
AND cc.movie_id in (select id from title where title = 'The Movie')
)
)
AND c.id NOT IN -- except the actors in the given movie
(SELECT cc.id
FROM cast_info cc
INNER JOIN role_type rr
ON (cc.role_id = rr.id)
WHERE ( rr.role = 'actor') -- only select actors in the given movie
AND cc.movie_id in (select id from title where title = 'The Movie')
)
This easy to translate Natural language to SQL but not efficient using IN, you can transfer IN with EXISTS, that would be more efficient.
So we meet again. Hopefully I can be of the same assistance as your last question. I like that you began by breaking this down into smaller parts. I would have started at the same place as you - getting all of the actors in the necessary movie. I've changed your query slightly, by selecting the persons id instead of their name so I can keep this answer a little shorter and cleaner.
SELECT c.person_id
FROM cast_info c
JOIN title t ON c.movie_id = t.id
JOIN role_type r ON c.role_id = r.id
WHERE t.title = 'The Movie' AND (r.role = 'actress' OR r.role = 'actor');
*Note, I did not use distinct here because a person should not appear in this movie as an actor or actress more than once, but c.person_id is not a unique key so it would not hurt to be sure.
Then we can get all of the movies with those actors in them. We can filter to avoid the original table.
SELECT t.id
FROM title t
JOIN cast_info c ON c.movie_id = t.id
WHERE t.title != 'The Movie'
AND c.person_id IN(SELECT c.person_id
FROM cast_info c
JOIN title t ON c.movie_id = t.id
JOIN role_type r ON r.id = c.role_id
WHERE t.title = 'The Movie' AND (r.role = 'actress' OR r.role = 'actor'))
Now we can pull all of the actors from all of those movies, and exclude our original actors. Here is where it is a good idea to use distinct person id. This is because Bob may star in 'The Movie'. Later, Bob did a movie with John, and another movie with John, but we don't want John to appear twice.
So, here is the final query:
SELECT DISTINCT c.person_id
FROM cast_info c
WHERE c.movie_id IN(SELECT t.id
FROM title t
JOIN cast_info c ON c.movie_id = t.id
WHERE t.title != 'The Movie'
AND c.person_id IN(SELECT c.person_id
FROM cast_info c
JOIN title t ON c.movie_id = t.id
JOIN role_type r ON r.id = c.role_id
WHERE t.title = 'The Movie' AND (r.role = 'actress' OR r.role = 'actor')))
AND c.person_id NOT IN(SELECT c.person_id
FROM cast_info c
JOIN title t ON c.movie_id = t.id
JOIN role_type r ON r.id = c.role_id
WHERE t.title = 'The Movie' AND (r.role = 'actress' OR r.role = 'actor'))
As I've said before, it's difficult to test this without data, and it's a lot of tables to make a simple SQL Fiddle, so please try these in bits like I have written them if they don't work, and let me know what may need to be tweaked.
I'm trying to write a query to fetch a listing from 2 (list1,list2)tables with the same columns.
Are there any other way to rewrite this code?
(SELECT r.id as rid, s.title, u.username
FROM list1 r
JOIN drama s ON r.parent_id = s.id
LEFT JOIN image i ON s.image_id = i.id
LEFT JOIN user u ON r.user_id = u.user_id)
UNION ALL
(SELECT r.id as rid, s.title, u.username
FROM list2 r
JOIN movie s ON r.parent_id = s.id
LEFT JOIN image i ON s.image_id = i.id
LEFT JOIN user u ON r.user_id = u.user_id)
ORDER BY rid LIMIT 10
whats wrong if you do this
SELECT r.id as rid, m.title AS movie Title,
d.title as DramaTitle, u.username
FROM list1 r
INNER JOIN movie m ON r.parent_id = m.id
INNER JOIN drama d ON r.parent_id = d.id
LEFT JOIN image i ON s.image_id = i.id
LEFT JOIN user u ON r.user_id = u.user_id
ORDER BY r.id LIMIT 10
SELECT r.id as rid, s.title, u.username
FROM (SELECT l1.id, l1.user_id, l1.parent_id FROM list1 l1
UNION ALL
SELECT l2.id, l2.user_id, l2.parent_id FROM list2 l2) r
INNER JOIN drama s ON r.parent_id = s.id
LEFT JOIN image i ON s.image_id = i.id
LEFT JOIN user u ON r.user_id = u.user_id
ORDER BY rid
LIMIT 10