Find avg of rating for each item - mysql

I have a table with feilds : file_id, rating, user_id
There is one rating per user_id, so there could be many rating (in scale of 0-5) for a single file_id.
I want to find avg of ratings for every file_id and then display 5 file_id with highest avg rating.
Actually my sql query looks like:
SELECT m.server_domain, m.original_name, m.type, m.title, m.views,
m.description, m.hash, AVG(mr.rating_scale5) as avg_rating_scale5
FROM c7_media m, c7_storage s, c7_media_ratings mr
WHERE s.public=1 AND m.storage_hash = s.hash AND m.hash = mr.media_hash
GROUP BY mr.media_hash
How should I do this?
Zeeshan

Group by a file_id and then simply order by the average. Cut off all records that fall below the top 5.
SELECT
file_id, AVG(rating) as avg_rating
FROM
table
GROUP BY
file_id
ORDER BY
avg_rating DESC
LIMIT 5

SELECT `file_id`, AVG(`rating`) as a FROM `table`
GROUP BY `file_id` ORDER BY a DESC LIMIT 5
Replace 'table' with the name of your table.

Related

Combine two SQL statements with arithmetic (not joining per se)

I have a plugin that counts helpful votes for reviews on my site ('helpful', 'funny', 'cool', and it gives the option of adding other descriptors as well. So I've added 'not helpful'.
Now I'm trying to customize the query used to display the reviews with the most votes by taking 'not helpful' votes into account (subtracting them from the helpful votes) instead of just counting a total of all votes.
So originally the query used for this was:
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` GROUP BY review_id ORDER BY count DESC LIMIT 5
And I've found that I can count the helpful votes (where the number in the 'rate' column is 0, 1, or 2), like so:
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` <3 GROUP BY review_id ORDER BY count DESC LIMIT 5
And I can count the non-helpful votes (where the number in the 'rate' column is 3), like so:
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` >2 GROUP BY review_id ORDER BY count DESC LIMIT 5
But what I can't figure out is how to combine these two select statements such that the top 5 'count' results show a difference of the counts from each query.
I don't want a union because no subtraction is done, and I've tried various permutations of multiple selects, but can't manage to work this out.
Any suggestions?
An example table: Example Table
In the example above, if all votes are counted blindly, review #4 is ranked higher than review #10, but if not-helpful votes are taken into account, #10 is ranked higher with a net total of 1 versus review #4's net total of 0.
Make sense?
try this:
select a.review_id,b.review_id,a.count,b.count from
(SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` <3 GROUP BY review_id ORDER BY count DESC LIMIT 5) as a
left join
(SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` >2 GROUP BY review_id ORDER BY count DESC LIMIT 5) as b
on a.review_id = b.review_id
this is work if review_id of first query = review_id of second query otherwise you can use also <> to both.
other option use union all
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` <3 GROUP BY review_id ORDER BY count DESC LIMIT 5
union all
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` >2 GROUP BY review_id ORDER BY count DESC LIMIT 5

how can I get a DISTINCT from mySQL but only for 1 field?

I have a row with products and I'd like to get 10 random records, but a maximum of 1 row per user_id. Right now I have this:
SELECT user_id, product_id, price, name, category, is_featured
FROM db_products
WHERE category!=15 AND active=1 AND deleted=0 AND is_featured=1
ORDER BY RAND() DESC LIMIT 0,12
I tried doing a SELECT DISTINCT user_id, ... but that doesn't work. The table has 100's of products and each user_id may have multiple ones, but I'd like to retrieve a maximum of 1 per user_id, but still a total of 10.
Is that possible at all without a more complex structure?
I may be missing something, but have you tried doing a GROUP BY?
SELECT user_id, product_id, price, name, category, is_featured
FROM db_products
WHERE category!=15 AND active=1 AND deleted=0 AND is_featured=1
GROUP BY user_id -- SPECIFY FIELD HERE
ORDER BY RAND() DESC
LIMIT 0,12
This will group one row per user, or whichever field you desire to group by.
Try something like that with grouping in main query after random ordering in subquery:
SELECT * FROM
(SELECT user_id, product_id, price, name, category, is_featured
FROM db_products
WHERE category!=15 AND active=1 AND deleted=0 AND is_featured=1
ORDER BY RAND()) AS subquery
GROUP BY user_id
LIMIT 0,10

Sort by average rating between 2 values

I have a table called ranks which stores a 1-7 value and connects it with an item and user id. An item's rating is the average of all ratings it has been given.
Ranks table: id | userid | value | itemid.
To sort by average highest rating, I do (from an answer here):
select avg(value), itemid
from ranks
group by itemid
order by avg(value) desc
However, I also want to be able to filter items with a certain rating, not just highest rated -> lowest rated. For example, items with an average rating beteen 5 and 6.
select avg(value), itemid
from ranks
having avg(value) between 5 and 6
group by itemid
order by avg(value) desc
This gives me a nonspecific error at group by. Can anyone point me in the right direction?
Your having clause needs to come after your group by clause, so just reverse the order they appear in:
select avg(value), itemid
from ranks
group by itemid
having avg(value) between 5 and 6
order by avg(value) desc
select * from
(
select avg(value) as avgval, itemid
from ranks
group by itemid) t
where avgval between 5 and 6;
You can do it with a sub-query.
with Temp as
(
select avg(value) [rating], itemid
from ranks
)
Select * from Temp
Where rating between 5 and 6
group by itemid
order by rating desc

Select sum of top three scores for each user

I am having trouble writing a query for the following problem. I have tried some existing queries but cannot get the results I need.
I have a results table like this:
userid score timestamp
1 50 5000
1 100 5000
1 400 5000
1 500 5000
2 100 5000
3 1000 4000
The expected output of the query is like this:
userid score
3 1000
1 1000
2 100
I want to select a top list where I have n best scores summed for each user and if there is a draw the user with the lowest timestamp is highest. I really tried to look at all old posts but could not find one that helped me.
Here is what I have tried:
SELECT sum(score) FROM (
SELECT score
FROM results
WHERE userid=1 ORDER BY score DESC LIMIT 3
) as subquery
This gives me the results for one user, but I would like to have one query that fetches all in order.
This is a pretty typical greatest-n-per-group problem. When I see those, I usually use a correlated subquery like this:
SELECT *
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3;
This is not the whole solution, as it only gives you the top three scores for each user in its own row. To get the total, you can use SUM() wrapped around that subquery like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId;
Here is an SQL Fiddle example.
EDIT
Regarding the ordering (which I forgot the first time through), you can just order by totalScore in descending order, and then by MIN(timestamp) in ascending order so that users with the lowest timestamp appears first in the list. Here is the updated query:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
and here is an updated Fiddle link.
EDIT 2
As JPW pointed out in the comments, this query will not work if the user has the same score for multiple questions. To settle this, you can add an additional condition inside the subquery to order the users three rows by timestamp as well, like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score
AND mT.timeCol <= m.timeCol) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
I am still working on a solution to find out how to handle the scenario where the userid, score, and timestamp are all the same. In that case, you will have to find another tiebreaker. Perhaps you have a primary key column, and you can choose to take a higher/lower primary key?
Query for selecting top three scores from table.
SELECT score FROM result
GROUP BY id
ORDER BY score DESC
LIMIT 3;
Can you please try this?
SELECT score FROM result GROUP BY id ORDER BY score DESC, timestamp ASC LIMIT 3;
if 2 users have same score then it will set order depends on time.
You can use a subquery
SELECT r.userid,
( SELECT sum(r2.score)
FROM results r2
WHERE r2.userid = r.userid
ORDER BY score DESC
LIMIT 3
) as sub
FROM result r
GROUP BY r.userid
ORDER BY sub desc
You should do it like this
SELECT SUM(score) as total, min(timestamp) as first, userid FROM scores
GROUP BY userid
ORDER BY total DESC, first ASC
This is way more efficient than sub queries. If you want to extract more fields than userid, then you need to add them to the group by.
This will of cause not limit the number of scores pr user, which indeed seems to require a subquery to solve.

MySQL subquery for values in ORDER BY FIELD

I'm trying to write a query which will get the maximum scores for the most popular songs, in order of song popularity.
I have a table called "gameplay" with the following fields:
id
song_id
score
(plus some other arbitrary fields)
So far, the closest I've got is this:
SELECT id, song_id, score
FROM (
SELECT id, song_id, score
FROM (
SELECT id, song_id, score
FROM gameplay
ORDER BY score DESC
) AS all_scores_in_order
GROUP BY song_id
) AS top_scores_per_song
ORDER BY FIELD (song_id, 3,1,2)
But I would like the values in ORDER BY FIELD to be generated by another subquery - the song_id ranked by popularity (in order of counting row occurrences in table) ie.
SELECT song_id
FROM gameplay
GROUP BY song_id
ORDER BY count( id ) DESC
Can't you just group and then order by Count(id) in the outer query?
This should show all sorts sorted by the number of occurrences, and showing the max score:
SELECT song_id, COUNT(id), MAX(score)
FROM gameplay
GROUP BY song_id
ORDER count(id) DESC
Or do you want each song_id to appear several times?