MySQL - Joins and Between - mysql

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;

Related

Who are the top two actors whose movies have a median rating >= 8?

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;

SQL : Calculating Percentage by joining a sub table to another

I've the above dataset, I need to report for each year the percentage of movies in that year with only female actors, and the total number of movies made that year. For example, one answer will be: 1990 31.81 13522 meaning that in 1990 there were 13,522 movies, and 31.81%
In order to get the moves with only female actors, wrote the following code:
SELECT a.year as Year, COUNT(a.title) AS Female_Movies, a.title
FROM Movie a
WHERE a.title NOT IN (
SELECT b.title from Movie b
Inner Join M_cast c
on TRIM(c.MID) = b.MID
Inner Join Person d
on TRIM(c.PID) = d.PID
WHERE d.Gender='Male'
GROUP BY b.title
)
GROUP BY a.year,a.title
Order By a.year asc
The total movies in each year , can be found using the following:
SELECT a.year, count(a.title) AS Total_Movies
FROM Movie a
GROUP BY a.year
ORDER BY COUNT(a.title) DESC
Combinig the both I wrote, the following code:
SELECT z.year as Year, count(z.title) AS Total_Movies, count(x.title) as Female_movies, count(z.title)/ count(x.title) As percentage
FROM Movie z
Inner Join (
SELECT a.year as Year, COUNT(a.title) AS Female_Movies, a.title
FROM Movie a
WHERE a.title NOT IN (
SELECT b.title from Movie b
Inner Join M_cast c
on TRIM(c.MID) = b.MID
Inner Join Person d
on TRIM(c.PID) = d.PID
WHERE d.Gender='Male'
GROUP BY b.title
)
GROUP BY a.year,a.title
Order By a.year asc
)x
on x.year = z.year
GROUP BY z.year
ORDER BY COUNT(z.title) DESC
However, in th output I'm seeing the years with only female movies correctly, but the count of total movies is equal to female_movies so I'm getting 1%, I tried debugging the code, but not sure where this is going wrong. Any insights would be appreciated.
You assume that your 'z' contains all movies but since you do an inner join on the female movies, they'll also only contain female movies. You could fix that with a 'left join'.
Assuming your two queries are correct, you can join on them with a 'WITH' like this:
WITH allmovies (year, cnt) as
(SELECT a.year, count(a.title) AS Total_Movies
FROM Movie a
GROUP BY a.year
ORDER BY COUNT(a.title) DESC)
,
femalemovies (year, cnt, title) as
(SELECT a.year as Year, COUNT(a.title) AS Female_Movies, a.title
FROM Movie a
WHERE a.title NOT IN (
SELECT b.title from Movie b
Inner Join M_cast c
on TRIM(c.MID) = b.MID
Inner Join Person d
on TRIM(c.PID) = d.PID
WHERE d.Gender='Male'
GROUP BY b.title
)
GROUP BY a.year,a.title
Order By a.year asc)
select * from allmovies left join femalemovies on allmovies.year = femalemovies.year
You can use conditional aggregation. In a CASE expression check if no cast member that isn't female exists with a correlated subquery. If the check is successful, return something not NULL and count() that to get the number of movies with only female cast members (or none at all).
SELECT m.year,
count(*) count_all,
count(CASE
WHEN NOT EXISTS (SELECT *
FROM m_cast c
INNER JOIN person p
ON p.pid = c.pid
WHERE c.mid = m.mid
AND p.gender <> 'Female') THEN
1
END)
/
count(*)
*
100 percentage_only_female
FROM movie m
GROUP BY m.year;
Since in MySQL Boolean expressions in numerical context evaluate to 1 if true and to 0 otherwise, you could also use a sum() over the NOT EXISTS.
SELECT m.year,
count(*) count_all,
sum(NOT EXISTS (SELECT *
FROM m_cast c
INNER JOIN person p
ON p.pid = c.pid
WHERE c.mid = m.mid
AND p.gender <> 'Female'))
/
count(*)
*
100 percentage_only_female
FROM movie m
GROUP BY m.year;
That however isn't compatible with most other DBMS in contrast to the first one.
I would use two levels of aggregation:
SELECT m.MID, m.title, m.year,
COUNT(*) as num_actors,
SUM(gender = 'Female') as num_female_actors
FROM Movie m JOIN
M_cast c
ON c.MID = b.MID JOIN
Person p
ON p.PID = c.PID
GROUP BY m.MID, m.title, m.year;
Then a simple outer aggregation:
SELECT year,
COUNT(*) as num_movies,
SUM( num_actors = num_female_actors ) as num_female_only,
AVG( num_actors = num_female_actors ) as female_only_ratio
FROM (SELECT m.MID, m.title, m.year,
COUNT(*) as num_actors,
SUM(gender = 'Female') as num_female_actors
FROM Movie m JOIN
M_cast c
ON c.MID = b.MID JOIN
Person p
ON p.PID = c.PID
GROUP BY m.MID, m.title, m.year
) m
GROUP BY year;
Notes:
Use meaningful table aliases, rather than arbitrary letters. You'll note that the table aliases are abbreviations for the table names.
Do not use functions when filtering or JOINing unless necessary. I removed the TRIM(). If you need it use it. Or better yet, fix the data.
SELECT m.Year,COUNT(m.Year),x.t,
(COUNT(m.Year)*1.0/x.t*1.0)*100
FROM Movie m LEFT JOIN
(SELECT Year,COUNT(Year) AS t FROM Movie GROUP BY year) AS x
ON m.Year=x.Year
WHERE m.MID IN
(SELECT MID FROM M_Cast WHERE PID in
(SELECT PID FROM Person WHERE Gender='Female')
AND m.MID NOT IN
(SELECT MID FROM M_Cast WHERE PID in
(SELECT PID FROM Person WHERE Gender='Male'))) GROUP BY m.year
Check if this is what you're looking for.
select movie.year, count(movie.mid) as Year_Wise_Movie_Count,cast(x.Female_Cast_Only as real) / count(movie.mid) As Percentage_of_Female_Cast from movie
inner join
(
SELECT Movie.year as Year, COUNT(Movie.mid) AS Female_Cast_Only
FROM Movie
WHERE Movie.MID NOT IN (
SELECT Movie.MID from Movie
Inner Join M_cast
on TRIM(M_cast.MID) = Movie.MID
Inner Join Person
on TRIM(M_cast.PID) = Person.PID
WHERE Person.Gender!='Female'
GROUP BY Movie.MID
)
GROUP BY Movie.year
Order By Movie.year asc
) x
on x.year = movie.year
GROUP BY movie.year
ORDER BY movie.year
Output:
year Year_Wise_Movie_Count Percentage_of_Female_Cast
---- --------------------- -------------------------
1939 2 0.5
1999 66 0.0151515151515152
2000 64 0.015625
2018 104 0.00961538461538462
Note:
This was executed in SQLIte3

SQL - Select rows where one element appears twice

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
;

mysql filter by result of sub query

i have this Query :
SELECT a.*, a.id AS id_player, (SELECT COUNT(id) FROM `vd7qw_footsal_goals` WHERE a.id = id_player AND g.id = id_group) AS goals,
team.* FROM `vd7qw_footsal_players` AS a
LEFT JOIN vd7qw_footsal_teams AS team ON team.id= a.id_team
LEFT JOIN vd7qw_footsal_teamofgroup AS tog ON tog.id_team = team.id 4
LEFT JOIN vd7qw_footsal_groups AS g ON g.id = tog.id_group
WHERE g.id IN (SELECT id_group from `vd7qw_footsal_groupofleague` WHERE id_league = 2)
AND (a.state IN (1)) AND goals > 0 ORDER BY goals DESC
and i want to filter its resaults by players that have goals
the above query have error in this part goals > 0 i don't know how to do that can any1 help me ?
Try this:
SELECT * FROM
(SELECT a.*, a.id AS id_player, (SELECT COUNT(id) FROM `vd7qw_footsal_goals` WHERE a.id = id_player AND g.id = id_group) AS goals,
team.* FROM `vd7qw_footsal_players` AS a
LEFT JOIN vd7qw_footsal_teams AS team ON team.id= a.id_team
LEFT JOIN vd7qw_footsal_teamofgroup AS tog ON tog.id_team = team.id 4
LEFT JOIN vd7qw_footsal_groups AS g ON g.id = tog.id_group
WHERE g.id IN (SELECT id_group FROM `vd7qw_footsal_groupofleague` WHERE id_league = 2)
AND (a.state IN (1))) AS A WHERE goals > 0 ORDER BY goals DESC

Help with (somewhat simple) MySQL JOIN

Consider I have three tables:
Movies (movie 1, movie 2, etc)
Categories (action, suspense, etc)
Movies_Categories (movie 1 -> action, movie 1 -> suspense, movie 2 -> suspense, etc)
How could I select only the movies that belong or don't belong to a specific category using only 1 query?
Thanks!
Belongs:
SELECT m.*
FROM movies m
INNER JOIN movies_categories mc
ON m.id = mc.movie_id
INNER JOIN categories c
ON c.id = mc.category_id
AND c.name = 'action';
Doesn't belong:
SELECT m.*
FROM movies m
LEFT OUTER JOIN (SELECT mc.movie_id
FROM movies_categories mc
INNER JOIN categories c
ON c.id = mc.category_id
AND c.name = 'action') mcx
ON m.id = mcx.movie_id
WHERE mcx.movie_id IS NULL
SELECT m.*,
IF(mc.movie_id IS NULL, 'doesnt belong', 'belongs')
FROM Movies m
LEFT JOIN Movies_Categories mc ON mc.movie_id = m.id
AND mc.category_id = (SELECT id
FROM categories
WHERE name = 'action')