Movie with highest rating? - sql

Find the movie(s) with the highest average rating. Return the movie title(s) and average rating.
I tried this and stuck because I'm not able to retrieve mid if i add mid, max(avg_stars) then it will give max of every mid, I want only one max value.
http://sqlfiddle.com/#!3/e3ee1/13
select max(avg_stars) from
(
select top 1 mid, avg(stars) as avg_stars
from rating
group by mid
order by avg_stars desc
) z
excepted output Snow White 4.5 and how can i handle if two movies having same max(avg_stars).

This would serve your purpose perfectly & with performance -
SELECT
Title
,AVG_RATING
FROM
(
SELECT
M.Title
,M.mID
,CAST(ROUND(AVG(R.stars),2) AS DECIMAL(10,2)) AS AVG_RATING
,RANK() OVER (ORDER BY AVG(R.stars) DESC) RATING_RANK
FROM Movie M
INNER JOIN Rating R
ON M.mID = R.mID
GROUP BY M.Title,M.mID
)RANKED_RATING
WHERE RATING_RANK = 1
You may have to play around the casting a little to suit your table definitions.
Note - If 2 or more movies have the highest avg rating - all would be ranked 1 and all would get selected. If you still want only one - you'll need to define a rule as to which one you want to be selected.

Try this http://sqlfiddle.com/#!3/e3ee1/143:
;WITH CTE as
(
select r.mid, avg(r.stars) as avg_stars, m.title
from rating r
INNER JOIN Movie m ON m.mid=r.mid
group by r.mid, m.title
--order by avg_stars desc
)
select TOP 1 mid, title,avg_stars from CTE
Group by avg_stars,mid,title
--having avg_stars=Max(avg_stars)
Order By avg_stars desc
Output:
MID TITLE AVG_STARS
106 Snow White 4.5

SELECT TOP 1 MAX(m.title) AS title, AVG(stars) AS averageStars
FROM rating r
JOIN movie m
ON r.mId = m.mId
GROUP BY r.mId
ORDER BY AVG(stars) DESC,
--Order by a seond column of your
--choice to break ties for AVG(stars)
MAX(m.title)

You can probably optimize or come up with something cleaner but this works:
SELECT m.title, AVG(r.stars) AS AverageStars
FROM Rating AS r (NOLOCK)
INNER JOIN Movie AS m (NOLOCK) ON m.mID = r.mID
GROUP BY r.mID, m.Title
HAVING AVG(r.stars) =
(
SELECT TOP 1 AVG(stars) AS AverageStars
FROM Rating (NOLOCK)
GROUP BY mID
ORDER BY AverageStars DESC
)

Related

For each country, report the movie genre with the highest average rates

For each country, report the movie genre with the highest average ratings, and I am missing only one step that i cant figure it out.
Here's my current code:
SELECT c.code AS c_CODE, menres.genre AS GENRE, AVG(RATE) as AVERAGE_rate,MAX(RATE) AS MAXIMUM_rate, MIN(RATE) AS MINIMUM_rate from movirates
leftJOIN movgenres ON movgenres.movieid = movratings.movieid
left JOIN users ON users.userid = movrates.userid
left JOIN c ON c.code = users.city
LEFT JOIN menres ON movenres.genreid = menres.code
GROUP BY menres.genre , c.code
order by c.code asc, avg(rate) desc, menres.genre desc ;
You can use the ROW_NUMBER window function to assign a unique rank to each of your rows:
partitioned by country code
ordered by descendent average rating
Once you get this ranking, you may want to select all those rows which have the highest average rating (which are the same having the ranking equal to 1).
WITH cte AS (
SELECT c.code AS COUNTRY_CODE,
mg.genre AS GENRE,
AVG(rating) AS AVERAGE_RATING,
MAX(rating) AS MAXIMUM_RATING,
MIN(RATING) AS MINIMUM_RATING
FROM moviesratings r
INNER JOIN moviesgenres g ON g.movieid = r.movieid
INNER JOIN users u ON u.userid = r.userid
INNER JOIN countries c ON c.code = u.country
LEFT JOIN mGenres mg ON mg.code = g.genreid
GROUP BY mg.genre,
c.code
ORDER BY c.code,
AVG(rating) DESC,
mg.genre DESC;
)
SELECT *
FROM (SELECT *,
ROW_NUMBER() OVER(
PARTITION BY COUNTRY_CODE,
ORDER BY AVERAGE_RATING) AS rn
FROM cte) ranked_averages
WHERE rn = 1
Note: The code inside the common table expression is equivalent to yours. If you're willing to share your input tables, I may even suggest an improved query.
You should use window function in this case by using rank() then select the first rank only.
with mov_rates(c.code, genre, average, max, min)
as.
select c.code c_code,
e.genre genre,
avg (rate) avg
max (rate) max
min (rate) min
from movrates a
LEFT join movge.nres b on a.movieid = b.movieid
LEFT join users c on a.userid = c.user
LEFT join countr.ies d on c.code = d.code
left join mGenres e on b.genreid = e.code
group by d.country_code, e.x
),
rategenre (rank, c_code, genre, avgrate, max, min)
as
(
select rank() over (partition by c.c order by avgrates asc) rank,
country code,
genre,
average_r.ating,
maximum_rating,
minimum_.ating
from movrate \\just practicing on something
)
selec.t 2
from genre
where rank = 5
Reference:
OVER Clause

Problems with SQL Joins on a assigment

I've got a question for my assignment
Data
Question: For all cases where the same reviewer rated the same movie twice and gave it a higher rating the second time, return the reviewer's name and the title of the movie.
Here's what I've tried. I joined all the tables.
select *
from Rating
join Reviewer on Rating.rID = Reviewer.rID
join Movie on Rating.mID = Movie.mID
But how to continue? If a Reviewer rated the same Movie and the last rating of this movie is higher than former, then I need to show this reviewer. But how to do it in SQL?
Join what you already had with Rating again, so that you can get all records where the reviewer is the same and the movie is the same, then filter only rows where a record with a later ratingDate has more stars.
In case the same reviewer did review 3 or more times, then use select distinct to remove duplicates
select distinct rev.name, m.title
from Rating r1
join Reviewer rev on rev.rID = r1.rID
join Movie m on m.mID = r1.mID
join Rating r2 on r1.rID = r2.rID and r1.mID = r2.mID
where r1.ratingDate < r2.ratingDate and r1.stars < r2.stars
Here is a way to do this..
I find out the count of (rid,mid) combinations which have exactly 2 (ie two reviews by same reviewer against the same movie) this shows up as the column cnt.
After which i find the latest rating by ranking the ratingdate in desc. Thus row_number=1 gets you the latest rating value
with data
as (
select count(*) over(partition by rt.rid,rt.mid) as cnt
,row_number() over(partition by rt.rid,rt.mid order by rt.ratindate desc) as rnk
,rw.name
,mov.title
from rating rt
join reviewer rw
on rt.rid=rw.rid
join movie mov
on mov.mid=rt.mid
)
select *
from data
where rnk=1
and cnt=2
For the cases a reviewer rated the same movie multiple times, you are interested in their first and second rating. (Possible further ratings, i.e. a reviewer rating a movie a third or fourth time etc., must get ignored.) So, number the rows (with ROW_NUMBER). Then see whether the second rating is higher than the first (by grouping by reviewer and movie and comparing both ratings). For the matches look up reviewer name and movie title, for which you'd normally use where (rid, mid) in ( subquery ), but SQL Server does not support IN clauses with tuples, so you'd inner join instead.
select r.name, m.title
from reviewer r
cross join movie m
join
(
select rid, mid
from
(
select *, row_number() over(partition by rid order by ratingdate) as rn
from Rating
) numbered
group by rid, mid
having max(rn) > 1
and any_value(case when rn = 1 then stars end) <
any_value(case when rn = 2 then stars end)
) matches on matches.rid = r.rid and matches.mid = m.mid
order by r.name, m.title;

Shorten a query

I have to write a query that would calculate number of tickets purchased consisting only of movie genre of that type. At the end, I have to return movie genre and number of tickets bought for that genre. I have written a query but I was wondering if it can be made shorter and more compact?
Following is the database scheme:
movies(movieId, movieGenre, moviePrice)
tickets(ticketId, ticketDate, customerId)
details(ticketId, movieId, numOfTickets)
Here is my query:
select m.genre, count(*)
from(select t.ticketId, m.genre
from(select d.ticketId
from(select m.genre, t.ticketId
from tickets t join details d on t.ticketId =
d.ticketId join movies m on d.movieId = m.movieId
group by m.genre, t.ticketId) d
group by d.ticketId
having count(*) = 1) as t join details d on t.ticketId =
d.ticketId join movies m on d.movieId = m.movieId
group by t.ticketId, m.genre) m
group by m.genre;
This runs on a database so I am only able to post sample output:
comedy 29821
action 27857
rom-com 19663
I see no reason to use the table tickets, because the results do not filter or aggregate by ticketDate or customerID. Thus, a shorter sql is
SELECT m.moviegenre,
Sum(d.numoftickets) as SumNum
FROM details d
LEFT JOIN movies m
ON d.movieid = m.movieid
GROUP BY m.moviegenre
HAVING SumNum > 0
ORDER BY m.moviegenre
added 3/28 am
I am not sure what is meant by Duplicates?? In table = details(ticketId, movieId, numOfTickets) ??
I would expect that ticketId is unique, so what would explain duplicates?
Is the same ticketId being printed twice, repeatedly??
Determine what number of ticketId are duplicates--
SELECT ticketId, count(*) as cnt
FROM details d
GROUP By ticketId
HAVING count(*) > 1
Determine what number of "details" rows are duplicates--
SELECT ticketId, movieId, numOfTickets, count(*) as cnt
FROM details d
GROUP By ticketId, movieId, numOfTickets
HAVING count(*) > 1
Then again, it may be that table = movies(movieId, movieGenre, moviePrice) is the one with duplicates??
Determine what number of movieId are duplicates--
SELECT movieId, count(*) as cnt
FROM movies m
GROUP BY movieId
HAVING count(*) > 1
Remove duplicates from details--
SELECT m.moviegenre,
Sum(d.numoftickets) as SumNum
FROM
(Select Distinct * From details) d
LEFT JOIN movies m
ON d.movieid = m.movieid
GROUP BY m.moviegenre
ORDER BY m.moviegenre

MAX(COUNT(*)) for each ID in a SQL query

I'm needing some help with a SQL query using PostgreSQL 9.4.
I need the most rented movies on each local, this is the data I'm asked to select
movie title
year
local id
number of rents
Tables:
rental(idMovie, idLocal, idClient)
movies(idMovie, title, description, year)
This is what I have done, but is not what i am asked to do.
SELECT m.tile, m.year, r.idLocal, COUNT(*) AS cont
FROM rental r, movies m
WHERE m.idMovie=r.idMovie
GROUP BY r.idLocal, m.title, m.year
ORDER BY COUNT(*) DESC;
If I understand your question correctly what you want is to show the most rented out movie(s) for every location.
If that is the case then you could use a window function like rank() which will assign a ranking number to all movies based on the number of rentals for each location. The where clause then filters out the highest ranking movies (there can of course be more than one that shares the top spot).
select m.title, m.year, r.idLocal, r.rents
from movies m
join (
select
idMovie,
idlocal,
count(idmovie) rents,
rank() over (
partition by idlocal
order by count(idmovie) desc
) rn
from rental
group by idMovie, idLocal
) r on m.idMovie = r.idMovie
where rn = 1
order by idlocal;
Sample SQL Fiddle
Try this...
SELECT r.idLocal, m.title, m.year, count(r.idMovie) as count_movie
FROM rental r, movies m
WHERE m.idMovie=r.idMovie
GROUP BY r.idLocal, m.title, m.year
ORDER BY count_movie DESC;
Or
SELECT r.idLocal, m.title, m.year, count(r.idMovie) as count_movie
FROM rental r, movies m
WHERE m.idMovie=r.idMovie
GROUP BY r.idLocal, m.title, m.year
ORDER BY count(r.title) DESC;

expanding sql query for selecting top two rows based on rating criteria?

I am doing a few exercises to get my sql basics up. I am stuck here and unable to make any progress further. I would really appreciate if I could get tips on how to break down complex query such as the following:
There are three tables:
Movie ( mID, title, year, director ) --
There is a movie with ID number mID, a title, a release year, and a director.
Reviewer ( rID, name ) -- The reviewer with ID number rID has a certain name.
Rating ( rID, mID, stars, ratingDate ) -- The reviewer rID gave the movie mID a number of stars rating (1-5) on a certain ratingDate.
The problem is :
For all cases where the same reviewer rated the same movie twice and gave it a higher rating the second time, return the reviewer's name and the title of the movie.
Here is my attempt:
select distinct temp1.ID FROM (select * FROM
(select rID, name,title twos FROM
(select r.rID , rev.name, m.title, count(*) as twos from reviewer rev
JOIN rating r on r.rID=rev.rID
JOIN movie m on m.mID=r.mID
GROUP BY rev.rID) counts where counts.twos=2) result, rating r
where result.rID=r.rID ORDER BY ratingDate DESC) TEMP temp1
INNER JOIN TEMP temp2
ON temp1.rId = temp2.rId AND temp1.ratingDate > temp2.ratingDate
WHERE temp1.stars > temp2.stars;
I build this query iteratively. but It did not give right solution. so I would like to know how to approach this kind of problem.
This is NOT homework.I am doing online tutorial from here.
Thank you
In SQL, it helps to think in sets. For example you could select the set of reviews for which an earlier review with a lower rating exists:
select Reviewer.name
, Movie.title
from Rating
join Reviewer
on Reviewer.rID = Rating.rID
join Movie
on Movie.mID = Rating.mID
where exists
(
select *
from Rating prev
where prev.mID = Rating.mID
and prev.rID = Rating.rID
and prev.ratingDate < Rating.ratingDate
and prev.stars < Rating.stars
)
That's a really nice course btw!
First, you get rows with duplicate mID and rID combination. And then JOIN that to Rating to see if the second star given is higher than of the first. And then JOIN to Movie and Reviewer for the reviewer name and movie title.
SELECT
rv.name,
m.title
FROM (
SELECT
rID, mID
FROM Rating
GROUP BY rID, mID
HAVING COUNT(*) = 2
)t
INNER JOIN Rating r
ON r.rID = t.rID
AND r.mID = t.mID
INNER JOIN Rating r2
ON r2.rID = r.rID
AND r2.mID = r.mID
AND r2.ratingDate > r.ratingDate
INNER JOIN Movie m
ON m.mID = r.mID
INNER JOIN Reviewer rv
ON rv.rID = r.rID
WHERE
r2.stars > r.stars