SQL Division after HAVING-clausel - sql

i have a sql problem. I got two tables:
album (asin(pk), title, artist, price, release, label, rank)
track (album(pk), dsk, posn, song)
I need to find cheap albums where the price per song is under $0.5. How can i filter with a divion? My attempt so far:
SELECT a.title,a.price,COUNT(b.song)
FROM album a
JOIN track b
ON (asin=album)
GROUP BY album
HAVING COUNT(a.price / b.song)
I know that is wrong. I'm very new to SQL and it's confusing. I hope you can help me.
Thank you very much!

SELECT a.title, a.price, COUNT(b.song)
FROM album a
JOIN track b ON asin=album
GROUP BY a.title, a.price
HAVING 1.0 * a.price / COUNT(b.song) < 0.5
The JOIN condition asin=album looks fishy. Fix it by adding table aliases for the columns.

You need to group by a.asin (I suppose this is a unique album id, title potentially can be identical) because what you want is to group all the tracks into one line representing an album. COUNT(*) will count the number of rows grouped into this record (same as number of songs in the album). HAVING calculates average song price and tells to leave only albums cheaper than 0.5:
SELECT a.title, a.price, COUNT(*)
FROM album a
JOIN track b ON b.asin=b.album
GROUP BY a.asin
HAVING a.price / COUNT(*) < 0.5

Related

Which actor has the highest difference in ratings?

I need further help with my SQL problem.
In this database on movies, ratings and actors: https://i.stack.imgur.com/qFIbC.jpg
I am required to find the actor who has the largest difference between their best and their worst rated movie.
The condition is that the ratings cannot be lower than 3! (>3)
My current SQL looks as follows:
SELECT * FROM stars
JOIN ratings ON stars.movie_id = ratings.movie_id
WHERE ratings.movie_id = (
SELECT MAX(rating) - MIN(rating) FROM ratings
WHERE rating > 3);
My expectations were that I would get somewhat of a result in my Github terminal that I can work with to adjust my SQL query.
But I seem to have reached a dead-end and I'm not sure how to solve this solution
You need to GROUP BY actor to calculate everyone's rating range. Then, take the actor with the largest range. Something like this:
SELECT
person_id,
MAX(rating) - MIN(rating) AS rating_range
FROM
stars
JOIN ratings ON stars.movie_id = ratings.movie_id
WHERE
rating > 3
GROUP BY
person_id
ORDER BY
2 DESC
LIMIT
1
;

Get Average in SQL Through Join

I'm just playing around with SQL and I'm trying to do the following.
I have 2 tables and here is their structure:
Movies_metadata Movies
ratings table:
Ratings
As there are many ratings for one movie, what I'd like to do is get the avg rating per movie and have it display next to the title which is only available in the Metadata table.
This is as far as I got but obviously the issue with my SELECT statement is that it'll return the average of all movies and display it for each record:
SELECT
(SELECT
AVG(rating)
FROM
`movies-dataset.movies_data.ratings`) AS rating_avg,
metadata.title,
metadata.budget,
metadata.revenue,
metadata.genres,
metadata.original_language,
metadata.release_date
FROM
`movies-dataset.movies_data.Movies_metadata` AS metadata
INNER JOIN `movies-dataset.movies_data.ratings` AS ratings
ON metadata.id = ratings.movieId
LIMIT 10
Here is an example of the result:
Result
I'm thinking I can potentially use a GROUP BY but when I try, I get an error
Appreciate the help!
The following should work:
SELECT movies_metadata.title, AVG(ratings.rating)
FROM movies_metadata
LEFT JOIN ratings ON movies_metadata.id = ratings.movieID
GROUP BY movies_metadata.title
You can swap movies_metadata.title by movies_metadata.id if not unique.
The LIMIT function and GROUP function might conflict with each other. Try getting the average rating as part of the inner join like this:
SELECT
ratings.averagerating,
metadata.title,
metadata.budget,
metadata.revenue,
metadata.genres,
metadata.original_language,
metadata.release_date
FROM `movies-dataset.movies_data.Movies_metadata` AS metadata
INNER JOIN (SELECT movieId, AVG(rating) averagerating FROM `movies-dataset.movies_data.ratings` GROUP by movieId) AS ratings
ON metadata.id = ratings.movieId
ORDER BY ratings.averagerating
LIMIT 5
Maybe try something like:
Select m.movieID, (r.rate_sum / r.num_rate) as avg_rating
From your_movies_table m
Left Join (select movie_id, sum(rating) as ‘rate_sum’, count(rating) as ‘num_rate’
From your_ratings_table
Group by movie_id) r
On m.movie_id = r.movie_id
I'm using a left join because I'm not sure if all movies have been rated at least once.

Display 0 in count query using UNION and a subquery, Join not allowed

I have 2 tables (movies and watch).
I want to know which movies is being watched by how many people.
I did this:
SELECT movieID, count(persID) FROM watch GROUP BY persID;
Which gives me basically what I want. The only problem is that movies that aren't being watched by anyone won't show up in my result table as 0 viewers but they are just left out.
I want to achieve this in two different ways. Using UNION and the other way using a subquery.
So you got the list of movies that are being watched and the counter - left join it into the list of all movies:
SELECT * FROM
Movies m
LEFT JOIN
(select movieID, count(persID) as countwatch from watch group by persID) w
ON m.movieid = w.movieid
Left join means you get all the movies, linked up with counts of only those being watched. If a movie is not being watched it's countwatch column will be null
If you want to turn that null into a 0, use COALESCE:
SELECT m.*, COALESCE(w.countwatch, 0) as countwatch FROM
Movies m
LEFT JOIN
(select movieID, count(persID) as countwatch from watch group by persID) w
ON m.movieid = w.movieid
There are plenty of ways we could do this query, but I specifically chose this way because it builds on what you already did and know, and outlines how we can group and summarise data on a sub-level and then connect it to more data at an outer level
It's additionally useful because you might want to add some extra data in eg from the sales table to know which movies being watched are the highest earning ones. If you choose salman's route (which is right in this context) you'll encounter problems with the stats as you add more tables because row numbers will multiply more than you expect. By grouping eg the sales and the watching in subqueries you can join them to the main table without causing duplication of rows (also called a Cartesian product). As a result for queries of this nature I tend to suggest grouping and aggregating in subqueries before joining to other tables to preserve a 1:1 relationship between the main table (movies) and the outputs of subqueries (eg watching, sales, count of actors, etc) that contain data related to the main movie but not necessarily related to each other
Since you're insisting on using UNION and sub-query, here is an answer that uses both:
SELECT movieID, COUNT(*) AS watchCount
FROM watch
GROUP BY movieID
UNION
SELECT movieID, 0
FROM movies
WHERE movieID NOT IN (
SELECT movieID
FROM watch
WHERE movieID IS NOT NULL
)
try to do this
select movie.title, count(watch.persID)
from movies left outer join watch on movies.id = watch.movieID
group by movieID;
I think it could work
Using UNION only:
SELECT movieID, COUNT(*) -1 AS watchCount -- subtract the movie row
FROM
(
SELECT movieID -- multiple rows per watched movie
FROM watch
UNION ALL
SELECT movieID -- exactly one row per movie
FROM movies
)
or (might be faster if there are lots of rows in watch)
SELECT movieID, max(cnt)
FROM
(
SELECT movieID, count(*) as cnt
FROM watch
GROUP BY movieID
UNION ALL
SELECT movieID, 0
FROM movies
)
GROUP BY movieID

How can I rewrite a SQL query without any sub-queries?

How can I rewrite this query without using any subqueries in SQL?
I'm not too familiar with how to do this, but I think it's done by using "join."
SELECT title
FROM Movies Old
Where year < ANY
(SELECT year
FROM Movies
WHERE title = Old. title
);
(Note: this comes from the relation Movies(title, year, length, genre, studioName, producerC#))
To literally rewrite your current query using joins you can try this:
SELECT m1.title
FROM Movies m1
INNER JOIN Movies m2
ON m1.title = m2.title AND
m1.year < m2.year
But if all you really want is to find movie titles which appear more than once, then when not just use a GROUP BY query:
SELECT title
FROM Movies
GROUP BY title
HAVING COUNT(*) > 1

SQL to gather data from one table while counting records in another

I have a users table and a songs table, I want to select all the users in the users table while counting how many songs they have in the songs table. I have this SQL but it doesn't work, can someone spot what i'm doing wrong?
SELECT jos_mfs_users.*, COUNT(jos_mfs_songs.id) as song_count
FROM jos_mfs_users
INNER JOIN jos_mfs_songs
ON jos_mfs_songs.artist=jos_mfs_users.id
Help is much appreciated. Thanks!
The inner join won't work, because it joins every matching row in the songs table with the users table.
SELECT jos_mfs_users.*,
(SELECT COUNT(jos_mfs_songs.id)
FROM jos_mfs_songs
WHERE jos_mfs_songs.artist=jos_mfs_users.id) as song_count
FROM jos_mfs_users
WHERE (SELECT COUNT(jos_mfs_songs.id)
FROM jos_mfs_songs
WHERE jos_mfs_songs.artist=jos_mfs_users.id) > 10
There's a GROUP BY clause missing, e.g.
SELECT jos_mfs_users.id, COUNT(jos_mfs_songs.id) as song_count
FROM jos_mfs_users
INNER JOIN jos_mfs_songs
ON jos_mfs_songs.artist=jos_mfs_users.id
GROUP BY jos_mfs_users.id
If you want to add more columns from jos_mfs_users in the select list you should add them in the GROUP BYclause as well.
Changes:
Don't do SELECT *...specify your fields. I included ID and NAME, you can add more as needed but put them in the GROUP BY as well
Changed to a LEFT JOIN - INNER JOIN won't list any users that have no songs
Added the GROUP BY so it gives a valid count and is valid syntax
SELECT u.id, u.name COUNT(s.id) as song_count
FROM jos_mfs_users AS u
LEFT JOIN jos_mfs_songs AS S
ON s.artist = u.id
GROUP BY U.id, u.name
Try
SELECT
*,
(SELECT COUNT(*) FROM jos_mfs_songs as songs WHERE songs.artist=users.id) as song_count
FROM
jos_mfs_users as users
This seems like a many to many relationship. By that I mean it looks like there can be several records in the users table for each user, one of each song they have.
I would have three tables.
Users, which has one record for each user
Songs, which has one record for each song
USER_SONGS, which has one record for each user/song combination
Now, you can do a count of the songs each user has by doing a query on the intermediate table. You can also find out how many users have a particular song.
This will tell you how many songs each user has
select id, count(*) from USER_SONGS
GROUP BY id;
This will tell you how many users each song has
select artist, count(*) from USER_SONGS
GROUP BY artist;
I'm sure you will need to tweak this for your needs, but it may give you the type of results you are looking for.
You can also join either of these queries to the other two tables to find the user name, and/or artist name.
HTH
Harv Sather
ps I am not sure if you are looking for song counts or artist counts.
You need a GROUP BY clause to use aggregate functions (like COUNT(), for example)
So, assuming that jos_mfs_users.id is a primary key, something like this will work:
SELECT jos_mfs_users.*, COUNT( jos_mfs_users.id ) as song_count
FROM jos_mfs_users
INNER JOIN jos_mfs_songs
ON jos_mfs_songs.artist = jos_mfs_users.id
GROUP BY jos_mfs_users.id
Notice that
since you are grouping by user id, you will get one result per distinct user id in the results
the thing you need to COUNT() is the number of rows that are being grouped (in this case the number of results per user)