Hello I'm currently working on SQL problem that I can't quite figure out. Here is the Schema I'm working with:
Here is the question I am stuck on:
-- 3 Find the first name, last name and total combined film length of Sci-Fi films for every actor. That is the result should list the names of all of the actors (even if an actor has not been in any Sci-Fi films) and the total length of Sci-Fi films they have been in.
So far I have
SELECT actor.first_name, actor.last_name, (SELECT SUM(film.length)
from film
INNER JOIN film_category
ON film.film_id = film_category.film_id
INNER JOIN category
ON film_category.category_id = category.category_id
INNER JOIN film_actor
ON film_actor.film_id = film.film_id
INNER JOIN actor
ON film_actor.actor_id = actor.actor_id
WHERE category.name = 'Sci-fi'
)
from actor
I know I need to group it by actor_id but i'm unable to do this in a select subquery. Anyone have some tips?
There is no need to use a subquery. Aggregate functions work on the entire data set. The 'group by' specifies how to group the data you're aggregating.
select a.actor_id, a.first_name, a.last_name, sum(f.length)
from actor a
left outer join film_actor fa on fa.actor_id = a.actor_id
left outer join film f on f.film_id = fa.film_id
left outer join film_categories fc on fc.film_id = f.film_id
left outer join categories c on c.category_id = fc.category_id
where c.name = 'sci-fi'
group by a.actor_id
;
The outer joins ensure actors with no sci-fi film experience are included in the results by
I don't understand what are you need subquery, try this:
SELECT actor.first_name, actor.last_name,SUM(film.length)
from film
INNER JOIN film_category
ON film.film_id = film_category.film_id
INNER JOIN category
ON film_category.category_id = category.category_id
INNER JOIN film_actor
ON film_actor.film_id = film.film_id
INNER JOIN actor
ON film_actor.actor_id = actor.actor_id
WHERE category.name = 'Sci-fi'
GROUP BY actor.actor_id;
This should get you exactly what you want, including the part about having actors that aren't in Sci-Fi movies. You can LEFT JOIN on film to include all films the film_actor is in. The additional AND statement works with the LEFT JOIN to include actors not in Sci-Fi movies for your aggregate sum function.
SELECT a.actor_id, a.first_name, a.last_name, sum(f.length) AS length
FROM actor a
INNER JOIN film_actor fa ON fa.actor_id = a.actor_id
INNER JOIN film_category fc ON fc.film_id = fa.film_id
INNER JOIN category c ON c.category_id = fc.category_id
LEFT JOIN film f ON f.film_id = fa.film_id
AND c.name = 'Sci-Fi'
GROUP BY a.actor_id;
The top answer here actually is not correct. This will work:
SELECT T1.first_name, T1.last_name, T2.total
FROM
(SELECT a.first_name, a.last_name, a.actor_id
FROM actor a
)
AS T1
LEFT JOIN
(SELECT a.first_name, a.last_name, a.actor_id, SUM( f.length ) AS total, c.name
FROM actor a, film_actor fa, film f, film_category fc, category c
WHERE c.category_id = fc.category_id
AND fc.film_id = f.film_id
AND a.actor_id = fa.actor_id
AND fa.film_id = f.film_id
AND c.name = 'sci-fi'
GROUP BY a.actor_id)
AS T2
ON T1.actor_id = T2.actor_id;
It takes all actors, and combines them with the sci-fi actors and gives the combined screen time for sci-fi movies for all. Test it on your data set, worked for me.
Related
I am trying to put together a query from the Sakila database.
The query should find all actors that have starred in all 16 film categories.
To get all of this information into one table for querying, I have performed a INNER JOIN:
SELECT a.first_name, a.last_name FROM actor a
INNER JOIN film_actor fa
ON fa.actor_id = a.actor_id
INNER JOIN film_category fc
ON fc.film_id = fa.film_id;
However, from there I do a GROUP BY on the category_id but don't know how to iterate through and count if a particular actor_id has all 16 categories?
Does this complex of a query require writing a FUNCTION or PROCEDURE?
You are almost there. Group against the actor name and check that the unique category count is 16:
SELECT a.actor_id, a.first_name, a.last_name
FROM actor a
INNER JOIN film_actor fa ON fa.actor_id = a.actor_id
INNER JOIN film_category fc ON fc.film_id = fa.film_id
GROUP BY a.actor_id, a.first_name, a.last_name
HAVING COUNT(DISTINCT fc.category_id) =
(
SELECT COUNT(DISTINCT category_id)
FROM film_category
)
I am trying to find the correct SQL to perform my task. I am trying to find the count of all actors that all act in the same movie. For all the movies. My SQL statement to fetch the film title and the first and last name of each actor is.
SELECT title, actor.first_name, actor.last_name,
FROM film
INNER JOIN film_actor ON film.film_id = film_actor.film_id
INNER JOIN actor ON film_actor.actor_id = actor.actor_id
ORDER BY film.title
My database returns this :
As you can see there are 10 actors belonging to the movie title ACADEMY DINOSAUR. What is the SQL that would provide me with a number of actors for each film?
Your SQL can be like this
SELECT title, count(1) COUNT
FROM film
INNER JOIN film_actor ON film.film_id = film_actor.film_id
INNER JOIN actor ON film_actor.actor_id = actor.actor_id
GROUP BY title
Pls try this
select tt.title, count(1) from (
SELECT title, actor.first_name, actor.last_name,
FROM film
INNER JOIN film_actor ON film.film_id = film_actor.film_id
INNER JOIN actor ON film_actor.actor_id = actor.actor_id
) tt group by tt.title
ORDER BY tt.title
Using: http://dev.mysql.com/doc/sakila/en/
I'm trying to write a query to count the number of films each actor has acted in by category.
Would appreciate any pointers or advice.
Here is my code so far (it counts the films correctly for one category when I take out the second LEFT JOIN section):
SELECT actor.first_name, actor.last_name,
COUNT(subset.film_id) AS action,
COUNT(subset2.film_id) AS animation
FROM actor
LEFT JOIN (
SELECT film.film_id, actor.actor_id
FROM actor
INNER JOIN film_actor
ON film_actor.actor_id = actor.actor_id
INNER JOIN film
ON film.film_id = film_actor.film_id
INNER JOIN film_category
ON film_category.film_id = film.film_id
INNER JOIN category
ON category.category_id = film_category.category_id
WHERE category.name = 'Action') AS subset
ON subset.actor_id = actor.actor_id
LEFT JOIN (
SELECT film.film_id, actor.actor_id
FROM actor
INNER JOIN film_actor
ON film_actor.actor_id = actor.actor_id
INNER JOIN film
ON film.film_id = film_actor.film_id
INNER JOIN film_category
ON film_category.film_id = film.film_id
INNER JOIN category
ON category.category_id = film_category.category_id
WHERE category.name = 'Animation') AS subset2
ON subset2.actor_id = actor.actor_id
GROUP BY actor.actor_id
ORDER BY actor.last_name ASC;
The query you've got seems awfully complicated if all you want is to get a count of movies per actor and category.
This query:
SELECT
actor.first_name, actor.last_name, category.name,
COUNT(*) as CountPerCategory
FROM actor
JOIN film_actor ON film_actor.actor_id = actor.actor_id
JOIN film ON film.film_id = film_actor.film_id
JOIN film_category ON film_category.film_id = film.film_id
JOIN category ON category.category_id = film_category.category_id
GROUP BY actor.first_name, actor.last_name, category.name;
would give you an output like:
Firstname, Lastname, Category, CountPerCategory
Clint Eastwood Animation 1
Clint Eastwood Action 15
but if you want the counts per category in different columns you could exploit the fact that MySQL returns 1 for true conditions and test that the category matches and use sum (this could also be don't with a case expression in a more portable way), like this:
SELECT
actor.first_name, actor.last_name, category.name
, SUM(category.name = 'Animation') as CountOfAnimation
, SUM(category.name = 'Action') as CountOfAction
FROM actor
JOIN film_actor ON film_actor.actor_id = actor.actor_id
JOIN film ON film.film_id = film_actor.film_id
JOIN film_category ON film_category.film_id = film.film_id
JOIN category ON category.category_id = film_category.category_id
GROUP BY actor.first_name, actor.last_name, category.name;
which would give a result like:
Firstname, Lastname, CountOfAnimation, CountOfAction
Clint Eastwood 1 15
Note that if a film belongs to multiple categories it would get counted once for each category, which might be what you want (or not).
This should give you the count for all actors, with all categories they have been associated with:
SELECT actor.first_name, actor.last_name, category.name
, COUNT(DISTINCT film.film_id)
FROM actor
LEFT JOIN film_actor ON film_actor.actor_id = actor.actor_id
LEFT JOIN film ON film.film_id = film_actor.film_id
LEFT JOIN film_category ON film_category.film_id = film.film_id
LEFT JOIN category ON category.category_id = film_category.category_id
GROUP BY actor.first_name, actor.last_name, category.name
;
If you only want specific ones, add on this after the GROUP BY line:
HAVING category.name IN ('Action', 'Animation')
If you only want actors with those categories, change the LEFT JOINs to INNER JOINs and/or change the aforementioned HAVING to a WHERE (and reposition it accordingly).
I agree, that query looks very complicated and by using 2 derived tables it'll probably run slowly as well. This query will give the unique films in each category and will not duplicate the results as there are no many to many joins.
It will slow down as you add categories however because it has to scan the table multiple times but it will give accurate results and as long as your table isn't too large it should be fine.
You probably don't need the distinct's, but if for whatever reason someone had the same category on a film twice (or something similar) this will remove room for error.
select a.first_name, a.last_name
, (
select count(distinct f.id)
from film f
join film_actor fa on fa.film_id=f.film_id
join film_category fc on fc.film_id=f.film_id
join category c ON c.category_id = fc.category_id
where c.name='Animation'
and fa.actor_id=a.actor_id
) as unique_animation_films
, (
select count(distinct f.id)
from film f
join film_actor fa on fa.film_id=f.film_id
join film_category fc on fc.film_id=f.film_id
join category c ON c.category_id = fc.category_id
where c.name='Action'
and fa.actor_id=a.actor_id
) as unique_action_films
from actor a
Listed below is my code. It returns the count of each category that a person has been in for a movie. It returns the result but I would like it to list every category including the ones with 0 counts. I tried every combination of LEFT JOIN, OUTER JOIN, etc and it still doesn't work. Any help would be appreciated!
SELECT c.name, COUNT(f.title) FROM category c
LEFT JOIN film_category fc ON c.category_id = fc.category_id
INNER JOIN film f ON fc.film_id = f.film_id
INNER JOIN film_actor fa ON f.film_id = fa.film_id
INNER JOIN actor a ON fa.actor_id = a.actor_id
WHERE a.first_name = 'Jack' AND a.last_name = 'Daniel'
GROUP BY c.name ASC;
Try this. SQL Fiddle demo
SELECT c.name, COUNT(f.title) FROM category c
LEFT JOIN film_category fc ON c.category_id = fc.category_id
LEFT JOIN film f ON fc.film_id = f.film_id
LEFT JOIN film_actor fa ON f.film_id = fa.film_id
LEFT JOIN actor a ON fa.actor_id = a.actor_id and a.first_name = 'Jack' AND a.last_name = 'Daniel'
WHERE 1=1
GROUP BY c.name ASC;
Union the inverse criteria of your query, eg
SELECT c.name, COUNT(f.ti....
...
UNION
SELECT c.name, 0
FROM category c
...
WHERE NOT(a.first_name = 'Jack' AND a.last_name = 'Daniel')
...
I have 4 tables as follows which has the following attributes:
Actor:actor_id,firstname,lastname
film_actor:actor_id,film_id
film_category:film_id,category_id
category:category_id,name
I want to find the list of all actors working in films, their film_id, category_id and category name.
I want to use the In clause for foll query. So i am getting the o/p by implementing this as follows:
select a.first_name,a.actor_id,fc.film_id,c.name,c.category_id
from actor a,film_actor fa,film_category fc,category c where a.actor_id
in (select fa.film_id from film_actor fa where fa.actor_id=a.actor_id
and fa.film_id
in (select fc.film_id from film_category fc where fc.film_id=fa.film_id
and fc.category_id
in(select fc.category_id from category c where c.category_id=fc.category_id)))
But suppose now i want to know list of actors for particular category_id.Lets say suppose 5
which is present. So I make following changes:
select a.first_name,a.actor_id,fc.film_id,c.name,c.category_id
from actor a,film_actor fa,film_category fc,category c
where a.actor_id in
(select fa.film_id from film_actor fa where fa.actor_id=a.actor_id
and fa.film_id
in (select fc.film_id from film_category fc where fc.film_id=fa.film_id
and fc.category_id
in(select fc.category_id from category c where c.category_id=fc.category_id
and category_id=5)))
I am getting empty result.Also lastly when we should use IN clause and when should we not?
Don't use IN for this.. use JOIN instead.
select
a.first_name,
a.actor_id,
fc.film_id,
c.name,
c.category_id
from
actor a join
film_actor fa on fa.actor_id = a.actor_id join
film_category fc on fc.film_id = fa.film_id join
category c on c.category_id = fc.category_id and c.category_id = 5
I typically only use IN for a hard-coded set of IDs... JOIN or EXISTS for every other case. Not only is this cleaner, but it will likely result in a better performing execution plan as well.
Please start to use join syntax instead of an IN clause:
select a.first_name,
a.actor_id,
fc.film_id,
c.name,
c.category_id
from actor a
left join film_actor fa
on a.actor_id = fa.actor_id
left join film_category fc
fa.film_id = fc.film_id
left join category c
on fc.category_id = c.category_id
and c.category_id=5
This will return all records from the actor table regardless of if there is a matching record in the other tables.
If you need help learning JOIN syntax here is a great visual explanation of joins