Can I use a SUM function when inner joining several tables? - sql

I'm joining tables to view rental films by genre, but I also want to know how much was rented per genre using the SUM function. The amount of rental is within payment table. Several tables have been joined. So far, the data output shows me the columns I want but the last step is to view by total amount per genre. Here's my query:
This query returns genre, film_id, title and amount
SELECT category.name AS genre,film.film_id,film.title,payment.amount
FROM payment
INNER JOIN rental
ON payment.rental_id = rental.rental_id
INNER JOIN inventory
ON rental.inventory_id = inventory.inventory_id
INNER JOIN film
ON inventory.film_id = film.film_id
INNER JOIN film_category
ON film.film_id = film_category.film_id
INNER JOIN category
ON film_category.category_id = category.category_id
ORDER BY category.name

Does this get you what you need?
SELECT
category.name AS genre,
SUM(payment.amount)
FROM payment
INNER JOIN rental
ON payment.rental_id = rental.rental_id
INNER JOIN inventory
ON rental.inventory_id = inventory.inventory_id
INNER JOIN film
ON inventory.film_id = film.film_id
INNER JOIN film_category
ON film.film_id = film_category.film_id
INNER JOIN category
ON film_category.category_id = category.category_id
ORDER BY category.name
GROUP BY genre

Related

Postgres inner join or complicated selects?

Let's say I have the example below in the picture, with the intended result below the tables
So far, I am able to get the rental count with
SELECT inventory.film_id,
SUM((SELECT COUNT(*) FROM rental WHERE rental.inventory_id = inventory.inventory_id)) AS rentals,
(SELECT title FROM film WHERE film.film_id = inventory.film_id) AS title
FROM inventory
GROUP BY inventory.film_id
ORDER BY inventory.film_id
Result
film_id rentals title
1 23 Academy Dinosaur
2 7 Ace Goldfinger
. .. ................
I just cannot seem to figure out how to get lets say, category.name from linking film_id to film_category then to category. I have tried adding this code
INNER JOIN film_category ON inventory.film_id = film_category.film_id
but it just returns the same result, it doesn't join it. This wouldn't grab category.name anyways like I am needing.
Any help in understanding what the logic is behind this example would be awesome. thanks
Be careful here. A film can have many rentals and belong to many categories. So you want to join the number of rentals and the list of categories. This means, you should first aggregate your data and then join:
select f.film_id, f.title, c.categories, r.number_of_rentals
from film f
left join
(
select fc.film_id, string_agg(c.name, ', ' order by c.name) as categories
from film_category fc
join category c on c.category_id = fc.category_id
group by fc.film_id
) c on c.film_id = f.film_id
left join
(
select i.film_id, count(*) as number_of_rentals
from rental r
join inventory in on i.inventory_id = r.inventory_id
group by i.film_id
) r on r.film_id = f.film_id
order by f.film_id;
You can write simple query using JOIN
SELECT inventory.film_id, film.title, COUNT(*) AS rentals
FROM inventory
JOIN rental ON rental.inventory_id = inventory.inventory_id
JOIN film ON film.film_id = inventory.film_id
GROUP BY inventory.film_id, film.title
ORDER BY inventory.film_id;
PostgreSQL live example
If you need to get film categories use next version:
SELECT
inventory.film_id,
film.title,
COUNT(DISTINCT rental_id) AS rentals,
ARRAY_AGG(DISTINCT category.title)
FROM inventory
JOIN rental ON rental.inventory_id = inventory.inventory_id
JOIN film ON film.film_id = inventory.film_id
JOIN film_category ON film_category.film_id = inventory.film_id
JOIN category ON film_category.category_id = category.category_id
GROUP BY inventory.film_id, film.title
ORDER BY inventory.film_id;
fiddle here

sql query to find most viewed actor by the customer in the database

i would like to find the actor who is viewed most by the customer. There can be a scenario where we can get actor having same number of views but we will return the top one.
Here is what I have tried but it not working as intended:
SELECT count(film_actor.actor_id), actor.first_name, actor.last_name
FROM actor INNER JOIN film_actor ON actor.actor_id = film_actor.actor_id
GROUP BY film_actor.actor_id
Here is the database schema:
You have to look at actual film rentals:
SELECT COUNT(*) AS viewings, actor.actor_id, actor.first_name, actor.last_name
FROM actor INNER JOIN film_actor ON actor.actor_id = film_actor.actor_id
INNER JOIN film ON film_actor.film_id = film.film_id
INNER JOIN inventory ON film.film_id = inventory.film_id
INNER JOIN rental ON inventory.inventory_id = rental.inventory_id
GROUP BY actor.actor_id, actor.first_name, actor.last_name
ORDER BY viewings DESC
LIMIT 1

Using 1 Query to Display COUNT(Items) in a Column and COUNT(Same Item, but With Certain Value in Another Column)

Thanks in advance for the assistance!
I have three tables in a dummy database: inventory, film, and rental.
I need to know which film titles are not in the inventory. The only way to answer this that I can think of is by counting each item in the inventory and then comparing that count to the number of the same item that haven't been returned.
This returns three columns:
SELECT film.title, COUNT(inventory.film_id), (SELECT COUNT(inventory.film_id)
FROM inventory
INNER JOIN rental ON rental.inventory_id = inventory.inventory_id
INNER JOIN film on film.film_id = inventory.film_id
WHERE rental.return_date IS NULL)
FROM inventory
INNER JOIN rental ON rental.inventory_id = inventory.inventory_id
INNER JOIN film on film.film_id = inventory.film_id
GROUP BY film.title
Output:
"Graceland Dynamite" "6" "183"
"Opus Ice" "11" "183"
"Braveheart Human" "5" "183"
"Wonderful Drop" "9" "183"
...
As you can see, it shows me in the last column the TOTAL number of non-returned DVD's, while I want the total for that column.
If I run it and just look for the inventory count like so:
SELECT film.title, COUNT(inventory.film_id)
FROM inventory
INNER JOIN rental ON rental.inventory_id = inventory.inventory_id
INNER JOIN film on film.film_id = inventory.film_id
GROUP BY film.title
That works:
"Graceland Dynamite" "6"
"Opus Ice" "11"
"Braveheart Human" "5"
...
And if I run the same thing but counting only the inventory that has a NULL return date:
SELECT film.title, COUNT(inventory.film_id), rental.return_date
FROM inventory
INNER JOIN rental ON rental.inventory_id = inventory.inventory_id
INNER JOIN film on film.film_id = inventory.film_id
WHERE rental.return_date IS NULL
GROUP BY film.title, rental.return_date
That works too:
"Theory Mermaid" "1"
"Dances None" "2"
"Bound Cheaper" "1"
...
Please let me know if you can help me understand how to return the inventory count in one column and also the count of the non-returned items, and bonus points if you can tell me how to have a column that shows the difference between the two.
Also if there's a way to return only the rows where the difference is zero, that would be cool.
OR if I'm just thinking about this all wrong let me know too! I need all the help I can get.
Thanks!!
UPDATE:
I tried it like this too:
SELECT film.title, COUNT(inventory.film_id), x.copies_out
FROM (SELECT COUNT(rental.inventory_id) as copies_out
FROM rental
WHERE rental.return_date IS NULL) as x, inventory
INNER JOIN film ON film.film_id = inventory.film_id
INNER JOIN rental ON rental.inventory_id = inventory.inventory_id
GROUP BY film.title, x.copies_out, rental.inventory_id
HAVING COUNT(inventory.film_id) <= x.copies_out
This returns me 3 columns, one with the film title, one with the number of total copies listed in inventory, and one with the total number of rentals that still have not been returned (not the number of rentals with that inventory ID that haven't been returned yet.)
"Giant Troopers" "5" "183"
"Confessions Maguire" "2" "183"
"Vacation Boondock" "3" "183"
Please help!
-Pete
I think you are looking for a conditional aggregation using the filter clause:
SELECT film.title,
count(inventory.film_id),
count(distinct rental.inventory_id) filter (where rental.return_date is null)
FROM inventory
INNER JOIN rental ON rental.inventory_id = inventory.inventory_id
INNER JOIN film on film.film_id = inventory.film_id
GROUP BY film.title;
I need to know which film titles are not in the inventory.
This sounds like not exists:
select f.*
from films f
where not exists (select 1
from inventory i
where i.film_id = f.film_id
);
The rental table is not needed for this query.
If you need the rental table to identify films in the inventory, then you cannot answer your question. The closest you can come is "What films have not been rented?" That would be:
select f.*
from films f
where not exists (select 1
from inventory i join
rental r
on i.inventory_id = r.inventory_id
where r.film_id = f.film_id
);

PostgreSQL - Which film is the most popular in category “Sports”?

I'm trying to answer a specific question "Which film is the most popular in category “Sports”?"
I've tried this
WITH CustomerRentalsPerStore AS
(
SELECT R.customer_id, I.category_id, COUNT (R.inventory_id) as rental_count
from rental AS R
INNER JOIN inventory AS I
on R.inventory_id = I.inventory_id
GROUP BY customer_id, I.category_id
--ORDER BY COUNT (R.inventory_id) desc
)
SELECT c.customer_id, c.first_name, c.last_name, cr.rental_count, cr.store_id
FROM Customer C
INNER JOIN CustomerRentalsPerStore CR
on C.customer_id = CR.customer_id
where cr.rental_count = (SELECT MAX(rental_count) FROM CustomerRentalsPerStore)
AND CR.category_id='Sports'
Here are the ER Diagrams:
Any help would be appreciated! Thank you
Based on what you clarified for me the film with the highest number of rentals in the sports category, I have the following untested SQL that should give you the result:
SELECT f.title, COUNT(*) AS RentalCount
FROM film f
INNER JOIN film_category fc ON fc.film_id = f.film_id
INNER JOIN category c ON c.category_id = fc.category_id
INNER JOIN inventory i ON i.film_id = f.film_id
INNER JOIN rental r ON r.inventory_id = i.inventory_id
WHERE (c.name = 'Sports')
GROUP BY f.title
ORDER BY 2 DESC;
This effectively gets the number of all rentals (COUNT) for all films in the Sports category. You obviously only want the first result so just limit the output to one row.
The code is untested but should point you in the correct direction.
A other approach would be to make/limit the selection as small as possible at first before going into GROUP BY and COUNT(*)processing.
The optimizer might choose a better execution method but it will be depending on indexing.
SELECT
film.title
, COUNT(*)
FROM (
SELECT
category.category_id
FROM
category
INNER JOIN
film_category
ON
category.category_id = film_category.category_id
INNER JOIN
film
ON
film_category.category_id = film.film_id
INNER JOIN
inventory
ON
film.film_id = inventory.film_id
INNER JOIN
rental
ON
inventory.film_id= rental.inventory_id
WHERE
category.name = 'Sports'
) AS alias
INNER JOIN
film
ON
alias.film_id = film.film_id
GROUP BY
film.title
ORDER BY
COUNT(*) DESC
LIMIT 1

A query by using count with multiple columns in SQL

I am pretty new to SQL.
I have a movie database. With the following tables with the following with their columns listed:
Category Table
columns - category_id, name, last_update
Film_Category Table
columns - film id, category id, last_update
Inventory Table
columns - inventory_id, film_id, store_id, last_update
Rental Table
columns - rental_id, rental_date, inventory_id, customer_id, return_date, staff_id, last_update
Film Table
columns - film_id, title
Question/ Issue
I wish to create a query that lists each movie, the film category it is classified in, and how often it is rented. I wish to use the data from the five tables as much as possible.
I want the table to output the film title column, the category name column and the count of how many times it is rented out. The output should be something like this:
title name rental_count
Alter Victory Animation 10
Goofy Movie Animation 20
Help would really be appreciated for this task!
use join and aggregate function count
select F.title,C.name,count(rental_id) as rental_count from Rental R
left join Inventory I on R.inventory_id=I.inventory_id
inner join Film_Category Fc on I.film_id=Fc.film_id
inner join Flim F on F.film_id=Fc.film_id
inner join Category C on Fc.category_id=C.category_id
group by F.title,C.name
WITH film_rents AS
(
SELECT I.film_id, COUNT(1) AS rental_count
FROM Inventory AS I
INNER JOIN Rental AS R ON R.inventory_id = I.inventory_id
GROUP BY I.film_id)
SELECT F.title, ISNULL(rental_count, 0 ) AS rental_count, C.name
FROM Film AS F
LEFT JOIN film_rents AS FR ON F.film_id = FR.film_id
INNER JOIN Film_Category AS FC ON FC.film_id = F.film_id
INNER JOIN Category AS C ON C.category_id = FC.category_id
this does what you asked, however I think what you really wants is more than this. I am saying this because you have a junction table Film_Category which means for one film there is one or more categories. in that case the query you asked for ( and above query) does not do the job for you. Asuming you are using SQL‌ Server 2017 you can use this:
WITH film_rents AS
(
SELECT I.film_id, COUNT(1) AS rental_count
FROM Inventory AS I
INNER JOIN Rental AS R ON R.inventory_id = I.inventory_id
GROUP BY I.film_id),
film_categories AS
(
SELECT FC.film_id, STRING_AGG(C.name, ',') AS categories
FROM Film_Category AS FC
INNER JOIN Category AS C ON C.category_id = FC.category_id
GROUP BY FC.film_id
)
SELECT F.title, ISNULL(rental_count, 0 ) AS rental_count, FC.categories AS [name]
FROM Film AS F
LEFT JOIN film_rents AS FR ON F.film_id = FR.film_id
INNER JOIN film_categories AS FC ON FC.film_id = F.film_id