SQL condition statement for strings - mysql

I am trying to Write a query that produces the title of every action film with a rating of PG-13, R, or NC-17.
Here is my SQL:
SELECT title
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
WHERE category.name = "Action"
WHERE film.rating = "PG-13" OR "R" OR "NC-17";
I believe the problem is in my syntax in my film.rating conditioning. I am new to SQL. I would appreciate if someone could show me in the right direction!

Try the following:
SELECT title
FROM film f INNER JOIN film_category fc ON f.film_id = fc.film_id
INNER JOIN category c ON fc.category_id = c.category_id
WHERE c.name = 'Action'
AND f.rating IN ('PG-13', 'R', 'NC-17');

You have several errors in your SQL. I would write this as:
SELECT f.title
FROM film f INNER JOIN
film_category fc
ON f.film_id = fc.film_id INNER JOIN
category c
ON fc.category_id = c.category_id
WHERE c.name = 'Action' AND f.rating IN ('PG-13', 'R', 'NC-17');
Notes:
Table aliases make the query easier to write and to read.
Qualify all column names when you have more than one table in the query.
WHERE is used at most one time per SELECT.
OR is for boolean expressions, not constants. You want IN.
Single quotes are the SQL standard for string delimiters.

Related

How to display Null result with a WHERE clause

Hey guys I'm new to SQL and having some difficultly. I'm hoping someone could clear some stuff up for me.
This is my issue. I want to display all of the categories that an actor has played in and the amount of films they have played in that category. So for example they have played in action movies 5 times. This is what I have so far:
SELECT c.name AS "Category_Name"
, Count(c.name) AS "Count"
FROM category c
JOIN film_category fc
ON c.category_id = fc.category_id
JOIN film f
ON fc.film_id = f.film_id
JOIN film_actor fa
ON f.film_id = fa.film_id
JOIN actor a
ON fa.actor_id = a.actor_id
WHERE a.first_name = "Kevin"
AND a.last_name = "Bloom"
GROUP
BY c.name
ORDER
BY c.name ASC;
This will display all of the categories and the amount of times "Kevin Bloom" has played in each however it will not display NULL values for categories he has not played in and I need it to. I have spend a few hours trying to figure this out but it either didn't help or I wasn't able to understand it.
From what I gather the WHERE clause is causing this issue. I also believe I will likely need to use a LEFT JOIN instead and possibly a sub query. I'm a little shaky on both of these things when used in conjunction. If anyone can offer some help to a first time learner I would really appreciate it!
SELECT c.name AS "Category_Name", Count(a.actor_id) AS "Count"
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 = 'Kevin' AND a.last_name = 'Bloom'
GROUP BY c.name
ORDER BY c.name ASC;
Per your comment, why to use AND versus WHERE is about how WHERE is evaluated when executing. The WHERE clause limits the entire result set by the condition(s) you specify. Whereas the ON conditions only limit what is allowed to match the records and not necessarily the entire results set when an OUTER JOIN is used. So If you put a WHERE condition in that limits results based on the RIGHT side of your LEFT JOIN it becomes and INNER JOIN because it tells SQL that you only want the results that match and because only some categories match that actor you would only get those categories. However by putting the condition in the ON clause of the JOIN your results are not limited and all categories will be returned but only actors matching your criteria will be considered.
You would use a LEFT JOIN, but you have to be careful:
SELECT c.name AS Category_Name, Count(a.actor_id) AS "Count"
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 = 'Kevin' AND a.last_name = 'Bloom'
GROUP BY c.name
ORDER BY c.name ASC;
Notes:
LEFT JOIN is key to the solution.
Notice the COUNT() has changed to count the id from actor. This will return 0 for categories where he has not acted.
The standard delimiter for strings in SQL is a single quote, not a double quote.
There is no need to escape column aliases, unless necessary.

SQL with Multiple Left Joins

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

MySQL - Movie Database Query including Null Rows

For reference I am working on the Sakila rental DVD database outlined here (#2): http://www.ntu.edu.sg/home/ehchua/programming/sql/SampleDatabases.html
I am trying to find the runtime of all Sci-Fi movies each actor has been in, including those who have not been in a Sci-Fi movie. I have the correct query for those who HAVE been in a Sci-Fi movie, but I'm having trouble expanding it to include all actors even if the runtime is NULL.
Here is my query:
SELECT act.first_name, act.last_name, SUM(fm.length)
FROM film fm
INNER JOIN film_actor fa ON fa.film_id = fm.film_id
INNER JOIN actor act ON fa.actor_id = act.actor_id
LEFT JOIN film_category fc ON fm.film_id = fc.film_id
LEFT JOIN category cat ON fc.category_id = cat.category_id
WHERE cat.name = 'Sci-Fi'
GROUP BY act.first_name, act.last_name
ORDER BY act.last_name ASC
This gets me all 167 actors who are in at least 1 Sci-Fi movie. I think my WHERE clause is not allowing NULL rows, but I don't know how to fix it.
You are correct, the where clause is working on the far right side of your left joins...if it is null it's going to be filtered out because null does not equal 'Sci-Fi'. Want nulls? Tell the where statement to include them
WHERE (cat.name = 'Sci-Fi' or cat.name is null)
You should have "cat.name = 'Sci-Fi' be a part of your ON clause in the outer join.
Unless your criteria is filtering in on nulls, you should never put criteria involving an outer joined table in the WHERE clause, as it turns the outer join into an inner join. The criteria belongs in the ON clause.
SELECT act.first_name, act.last_name, SUM(fm.length)
FROM film fm
INNER JOIN film_actor fa
ON fa.film_id = fm.film_id
INNER JOIN actor act
ON fa.actor_id = act.actor_id
LEFT JOIN film_category fc
ON fm.film_id = fc.film_id
LEFT JOIN category cat
ON fc.category_id = cat.category_id
and cat.name = 'Sci-Fi'
GROUP BY act.first_name, act.last_name
ORDER BY act.last_name ASC

Converting MySQL code to Access: GROUP_CONCAT and a triple JOIN

I'm having a really hard time translating a piece of MySQL-code to Access.
I'm trying to use one of the queries found in the Sakila (MySQL) Database for an Access project I'm working on.
First of all, the GROUP_CONCAT function doesn't work at all. After some Google searches I found out that Access doesn't support this function but I couldn't find a working alternative. CONCAT however could be replaced by a few '+' operators.
Then comes the triple LEFT JOIN which kept returning a missing operator error. I found a blog post explaining how a series of brackets could help, but this resulted in even more trouble and prompted me to remove the brackets after which it threw more missing operator errors.
Also, SEPARATOR doesn't seem to be accepted as well, but this could be due to GROUP_CONCAT not functioning.
Is there anyone willing to get me in the right direction? I've been struggling with this for way too long.
SELECT
a.actor_id,
a.first_name,
a.last_name,
GROUP_CONCAT(DISTINCT CONCAT(c.name, ': ',
(SELECT GROUP_CONCAT(f.title ORDER BY f.title SEPARATOR ', ')
FROM film f
INNER JOIN film_category fc
ON f.film_id = fc.film_id
INNER JOIN film_actor fa
ON f.film_id = fa.film_id
WHERE fc.category_id = c.category_id
AND fa.actor_id = a.actor_id
)
)
ORDER BY c.name SEPARATOR '; ')
AS film_info
FROM
actor AS a
LEFT JOIN film_actor AS fa ON a.actor_id = fa.actor_id
LEFT JOIN film_category AS fc ON fa.film_id = fc.film_id
LEFT JOIN category AS c ON fc.category_id = c.category_id
GROUP BY a.actor_id, a.first_name, a.last_name
The most commonly-cited Access alternative to the MySQL GROUP_CONCAT() function is Allen Browne's ConcatRelated() function, available here.
As for parentheses around JOINs, yes, Access SQL is fussy about those. Instead of
FROM
actor AS a
LEFT JOIN film_actor AS fa ON a.actor_id = fa.actor_id
LEFT JOIN film_category AS fc ON fa.film_id = fc.film_id
LEFT JOIN category AS c ON fc.category_id = c.category_id
try
FROM
(
(
actor AS a
LEFT JOIN
film_actor AS fa
ON a.actor_id = fa.actor_id
)
LEFT JOIN
film_category AS fc
ON fa.film_id = fc.film_id
)
LEFT JOIN
category AS c
ON fc.category_id = c.category_id

In clause in mysql

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