Since this obviously looks like a web-based system, I would strongly suggest a slight denormalization and tacking on 5 columns to the product table for
UserRatings, UserCount, AdminRatings, AdminCount, FinalRating
When any entries are added or updated to the ratings table, you could apply a simple update trigger, something like
update Product p,
( select r.product_id,
sum( is_admin_rating=FALSE, 1, 0 ) as UserCount,
sum( is_admin_rating=FALSE, rating, 0 ) as UserRatings,
sum( is_admin_rating=TRUE, 1, 0 ) as AdminCount,
sum( is_admin_rating=TRUE, rating, 0 ) as AdminRatings
from Ratings r
where r.product_id = ProductIDThatCausedThisTrigger
group by r.product_id ) as PreSum
set p.UserCount = PreSum.UserCount,
p.UserRatings = PreSum.UserRatings,
p.AdminrCount = PreSum.AdminCount,
p.AdminRatings = PreSum.AdminRatings,
p.FinalRating = case when PreSum.UserCount = 0 and PreSum.AdminCount = 0
then 0
when PreSum.UserCount = 0
then PreSum.AdminRatings / PreSum.AdminCount
when PreSum.AdminCount = 0
then PreSum.UserRatings / PreSum.UserCount
else
( PreSum.UserRatings / PreSum.UserCount * .4 )
/ ( PreSum.AdminRatings / PreSum.AdminCount * .6 )
end
where p.product_id = PreSum.product_id
This way, you will never have to do a separate join to the ratings table and do aggregations which will just get slower as more data is accumulated. Then your query can just use the tables and not have to worry about coalesce, your count per each and their ratings will be there.
The case/when for the FinalRatings is basically doing it all wrapped up as the combination of the user counts and admin counts can be 0/0, +/0, 0/+ or +/+
So, if no count for either, the case/when sets rating to 0
if only the user count has a value, just get that average rating (userRatings / userCounts)
if only the admin count has a value, get admin avg rating (adminRatings / adminCounts)
if BOTH have counts, you are taking the respective averages * .4 and * .6 respectively. This would be the one factoring adjustment you might want to tweak.
Although the query itself looks somewhat monstrous and confusing, if you look at the "PreSum" query, you are only doing it for the 1 product that has just been rated and basis from the trigger. Then, a simple update based on the results of that joined by the single product ID.
Getting this to work might offer a better long-term solution for you.