calculate average rating in sql server - sql

this is my table:
I want to fetch records of Those Vendor which contain ServiceDescription "Plaster" or Skills "Plaster" or
is in Category "Plaster" and also want to calculate averagerating of those Vendor.
note:If there is no review Of any vendor then also that records should come.
this is my query:
select * from UserDetails u
,VendorInCategory v
,CategoryMaster c
,Review rv
where v.CategoryId=c.Id
and u.Id=r.UserId
and u.Id=rv.VendorId
and v.VendorId=u.Id
and ((u.ServiceDescription like '%Plaster%' )
or (u.Skills like '%Plaster%')
or (c.Name like '%Plaster%'))
here problem in above query is i am not getting that vendor whose review is not there.
but i also want that vendor which does not contain review but matches my criteria.
UserDetails:
id Servicedescription Skills
1 Plaster plaster
2 construction construvtion
3 plaster plaster
4 null null(not vendor)
5 null null(not vendor)
Review
id CustomerId Vendorid rating
1 4 1 3
2 5 1 3
Expected output:
VendorId ServiceDescription Skills averagerating
1 plaster plaster 3
3 plaster plaster 0
Note:final output should in descending order of average rating

Here, try this:
SAMPLE DATA
create table UserDetails(
Id int,
ServiceDescription varchar(20),
Skills varchar(20)
)
create table Review(
Id int,
CustomerId int,
VendorId int,
Rating int
)
insert into UserDetails values(1, 'Plaster', 'plaster'),(2, 'construction', 'construction'),(3, 'plaster', 'plaster');
insert into Review values(1, 4, 1, 3),(2, 5, 1, 3);
SOLUTION
select
u.Id as VendorId,
u.ServiceDescription,
u.Skills,
isnull(sum(r.rating)/count(r.rating), 0) as AverageRating
from UserDetails u
left join Review r
on r.VendorId = u.id
where
u.ServiceDescription like '%plaster%'
or u.Skills like '%plaster%'
group by
u.Id,
u.ServiceDescription,
u.Skills
order by AverageRating desc

Many users have used the AVERAGE function to calculate the average of a series of data. But what do you do when you have summary data instead of individual responses and need to calculate an average? (For example, counts of the number of people who selected each rating on a 5-point rating scale like the rating of a product.)
How to Calculate a Weighted Average
Let’s say you want to get the average overall rating for each product:
For rating 1, (9) nine people.
For rating 2, (13) Thirteen people.
For rating 3, (1) one people.
Using the AVERAGE function would result in an average of 7.7. Of course, this doesn’t make any sense. We should expect an average within the range of the scale (1 to 5).
In order to correctly calculate the average overall response to each question, we need to:
Multiply the number of individuals selecting each rating by the
corresponding rating value (1 – 5)
Add the results of those calculations together.
Divide that result by the total number of responses to the question.
SAMPLE DATA:
create table #tableRatings(
Id int,
Rating numeric(18,6)
)
insert into #tableRatings values(1, 4.3),(2,3.3),(3,4.8);
SOLUTION:
SELECT
SUM(
case
WHEN FLOOR(rating) = 1 THEN rating
WHEN FLOOR(rating) = 2 THEN rating *2
WHEN FLOOR(rating) = 3 THEN rating *3
WHEN FLOOR(rating) = 4 THEN rating *4
WHEN FLOOR(rating) = 5 THEN rating *5
end
) / SUM(rating)
FROM #tableRatings
RESULT:
3.733870

Use aggregate function AVG():
Try this:
SELECT u.id, u.ServiceDescription, u.Skills, u.fullname, u.email, AVG(ISNULL(rv.rating, 0)) averagerating
FROM UserDetails u
INNER JOIN VendorInCategory v ON v.VendorId=u.Id
INNER JOIN CategoryMaster c ON v.CategoryId=c.Id
LEFT JOIN Review rv ON u.Id=rv.VendorId
WHERE (u.ServiceDescription LIKE '%Plaster%' OR u.Skills LIKE '%Plaster%' OR
c.Name LIKE '%Plaster%')
GROUP BY u.id, u.ServiceDescription, u.Skills, u.fullname, u.email
ORDER BY averagerating DESC;
EDIT
Other solution to implement this:
SELECT u.id, u.ServiceDescription, u.Skills, u.fullname, u.email,
ISNULL(rv.averagerating, 0) averagerating
FROM UserDetails u
INNER JOIN VendorInCategory v ON v.VendorId=u.Id
INNER JOIN CategoryMaster c ON v.CategoryId=c.Id
LEFT JOIN (SELECT rv.VendorId, AVG(rv.rating) averagerating FROM Review rv GROUP BY rv.VendorId) rv ON u.Id=rv.VendorId
WHERE (u.ServiceDescription LIKE '%Plaster%' OR u.Skills LIKE '%Plaster%' OR
c.Name LIKE '%Plaster%')
ORDER BY ISNULL(rv.averagerating, 0) DESC;

Related

How to sum up max values from another table with some filtering

I have 3 tables
User Table
id
Name
1
Mike
2
Sam
Score Table
id
UserId
CourseId
Score
1
1
1
5
2
1
1
10
3
1
2
5
Course Table
id
Name
1
Course 1
2
Course 2
What I'm trying to return is rows for each user to display user id and user name along with the sum of the maximum score per course for that user
In the example tables the output I'd like to see is
Result
User_Id
User_Name
Total_Score
1
Mike
15
2
Sam
0
The SQL I've tried so far is:
select TOP(3) u.Id as User_Id, u.UserName as User_Name, SUM(maxScores) as Total_Score
from Users as u,
(select MAX(s.Score) as maxScores
from Scores as s
inner join Courses as c
on s.CourseId = c.Id
group by s.UserId, c.Id
) x
group by u.Id, u.UserName
I want to use a having clause to link the Users to Scores after the group by in the sub query but I get a exception saying:
The multi-part identifier "u.Id" could not be bound
It works if I hard code a user id in the having clause I want to add but it needs to be dynamic and I'm stuck on how to do this
What would be the correct way to structure the query?
You were close, you just needed to return s.UserId from the sub-query and correctly join the sub-query to your Users table (I've joined in reverse order to you because to me its more logical to start with the base data and then join on more details as required). Taking note of the scope of aliases i.e. aliases inside your sub-query are not available in your outer query.
select u.Id as [User_Id], u.UserName as [User_Name]
, sum(maxScore) as Total_Score
from (
select s.UserId, max(s.Score) as maxScore
from Scores as s
inner join Courses as c on s.CourseId = c.Id
group by s.UserId, c.Id
) as x
inner join Users as u on u.Id = x.UserId
group by u.Id, u.UserName;

SQL: Select count of a record in right table with joins

I have 2 tables one for mobiles and other is for reviews. Reviews table store the reviews of a specific mobile against its mobile id.
Structure of mobiles table.
mobile_id | mobile_name
Structure of reviews table.
review_id | mobile_id | review_body
So far I have written this query.
SELECT c.*, p.review_body
FROM ((select mobile_id, mobile_name from mobiles
WHERE brand_id=1 limit 0,5) c)
left JOIN
(
SELECT mobile_id,
MAX(review_id) MaxDate
FROM reviews
GROUP BY mobile_id
) MaxDates ON c.mobile_id = MaxDates.mobile_id left JOIN
reviews p ON MaxDates.mobile_id = p.mobile_id
AND MaxDates.MaxDate = p.review_id
This query returns the first 5 mobiles from mobile table and their latest (one) review from review table. This is the result it returns.
mobile_id | mobile_name | review_body
Question: But i also want review_count with it. review_count should be equal to total number of reviews a mobile has in reviews table against its mobile_id.
So please tell me how it can be done with a single query that I already have. Any help would be appreciated as i am trying to do this since 24 hours.
I think this would work
SELECT c.*, p.review_body, MaxDates.review_count
FROM ((select mobile_id, mobile_name from mobiles
WHERE brand_id=1 limit 0,5) c)
left JOIN
(
SELECT mobile_id,count(review_id) review_count,
MAX(review_id) MaxDate
FROM reviews
GROUP BY mobile_id
) MaxDates ON c.mobile_id = MaxDates.mobile_id left JOIN
reviews p ON MaxDates.mobile_id = p.mobile_id
AND MaxDates.MaxDate = p.review_id

Using SELECT with UNION

CREATE TABLE members
(
name varchar(60),
ID char(6) PRIMARY KEY
);
CREATE TABLE ratings
(
memberID char(6) REFERENCES members(ID),
rating SMALLINT CHECK(rating >= 1 AND rating <= 8),
gameID integer REFERENCES games(ID),
PRIMARY KEY (memberID, gameID)
);
Hi guys, I'm trying to list all the members who have rated a game or not with showing the Max rate, Min rate,average rate, and how many times they have rated.
I tried :
(SELECT MAX(rating), MIN(rating), AVG(rating), COUNT(rating), name
FROM ratings, members
WHERE ratings.memberID = members.ID
GROUP BY name)
UNION
(SELECT MAX(rating), MIN(rating), AVG(rating),COUNT(distinct rating), name
FROM ratings, members
WHERE
members.ID NOT IN (SELECT memberID
FROM ratings, members
WHERE ratings.memberID = members.ID)
GROUP BY name);
This first part gives me a correct values; it gives the correct names followed by Max, min, and count, and the average. But the second part gives a correct names but wrong values of Max, Min, Average. It gives a Max of 9 and Min of 2 for all members who didn't rate any game! Which is not true. How can i fix the second part , so it gives a value of zero instead of 9 and 2 ?
I think you can get the result set you are looking for by using either a LEFT or RIGHT join
SELECT
M.name,
MAX(rating),
MIN(rating),
AVG(rating),
COUNT(rating)
FROM
[members] M
LEFT OUTER JOIN
[ratings] R
ON
M.ID = R.memberID
GROUP BY
M.name
This would then give a results like
name max | min | average | count
name1 8 | 2 | 5 | 3
name2 NULL | NULL | NULL | 0
I'd rewrite that query using INNER JOIN syntax, as it is clearer why the joins bring back unexpected results. As you'd written it, the second query would join all results against all members that had no results, per:
SELECT MAX(rating), MIN(rating), AVG(rating),COUNT(distinct rating), name
FROM ratings, members
WHERE
members.ID NOT IN (SELECT memberID
FROM ratings, members
WHERE ratings.memberID = members.ID)
I suspect you were after something more like:
SELECT MAX(rating),
MIN(rating),
AVG(rating),
COUNT(rating),
name
FROM ratings
INNER JOIN members
ON ratings.memberID = members.ID
GROUP BY name
UNION
SELECT MAX(rating),
MIN(rating),
AVG(rating),
COUNT(distinct rating),
name
FROM ratings
WHERE ratings.memberID NOT IN (SELECT memberID FROM members)
GROUP BY name

SQL select for average from another table

I spent a lot of time building this select, but Im not able to solve it. I have 2 tables. First table is called car and has PK (primary key) id_car and another columns name and so on. Second table is called rating and has colums id_rating (PK), id_car (FK) and rating_value (integer). As you suspect, one car can have more than one ranting. I want to select all cars and I want to know average rating to each car. Finally, I want to order the result by this average desc. I was trying things like this:
SELECT id_car, name, average
FROM car C, rating R
WHERE C.id_car = R.id_car
ORDER BY (average) (
SELECT AVG(rating_value) AS average
FROM rating R
WHERE C.id_car = R.id_car)
but it doesn't work.
For SQL Server; Also I suggest you to use JOIN instead of WHERE table1, table2..
SELECT C.id_car, name, AVG(rating_value) AS average
FROM car C JOIN rating R
ON C.id_car = R.id_car
GROUP By C.id_car, name
ORDER BY average DESC
This implements the aggregate function AVG() and then a GROUP BY the car id and name:
select c.id_car, c.name, avg(r.rating_value) aver
from car c
left join rating r
on c.id_car = r.id_car
group by c.id_car, c.name
order by aver desc
Using a LEFT JOIN will include all cars in the result even those which have no yet been rated.
SELECT C.name, AVG(R.rating_value)
FROM car C, rating R
WHERE C.id_car = R.id_car
GROUP BY C.name
ORDER BY AVG(R.rating_value) DESC
SELECT c.id_car, c.name,avg(r.rating_value) as rating
FROM car c
join rating r
on c.id_car = r.id_car
group by r.id_car
order by rating

complex sql query from 4 tables

I am developing an online travel guide with a lot of hotels. Each hotel belongs to a specific category, has a lot room types and each of hotel room has different price per season. I want to make a complex query from 4 tables in order to get the total number of hotels per hotels category where the minimum price of each hotel rooms is between 2 values which are adjusted by a slider.
My tables look like:
Categories
id_category
category_name
Hotels
id_hotel
hotel_name
category_id
......
hotels_room_types
id_hotels_room_type
hotel_id
room_type_id
......
hotels_room_types_seasons
hotels_room_types_id
season_id
price
......
for example some values of category_name are: Hotels, apartments, hostels
I would like my results table to have two fields like the following:
Hotels 32
apartments 0
hostels 5
I tried the following query but it returns the total number of all hotels per category, not the number of hotels where the minimum price of their rooms is between the price range.
SELECT c.category_name, count( DISTINCT id_hotel ) , min( price ) min_price
FROM categories c
LEFT JOIN hotels w ON ( c.id_category = w.category_id )
LEFT JOIN (
hotels_room_types
INNER JOIN hotels_room_types_seasons ON hotels_room_types.id_hotels_room_types = hotels_room_types_seasons.hotels_room_types_id)
ON w.id_hotel = hotels_room_types.hotel_id
GROUP BY c.category_name
HAVING min_price >=10 AND min_price <=130
Could anyone help me how to write the appropriate query?
Thanks!!!
SELECT Categories.Name, COUNT(DISTINCT ID_Hotel) [Count]
FROM Hotels
INNER JOIN Categories
ON Category_ID = ID_Category
INNER JOIN
( SELECT Hotel_ID, MIN(Price) [LowestPrice]
FROM hotels_room_types
INNER JOIN hotels_room_types_seasons
ON id_hotels_room_type = hotels_room_types_id
-- CONSIDER FILTERING BY SEASON HERE
GROUP BY Hotel_ID
) price
ON price.Hotel_ID = Hotels.ID_Hotel
WHERE LowestPrice BETWEEN 10 AND 130 -- OR WHATEVER YOUR PARAMETERS ARE
GROUP BY Categories.Name
I have no idea what RDBMS you are using but I do not know any where your query would work. The problem you were having with the Min Price (I assume) is because you are applying the logic after grouping by category, so you are counting all hotels where the category has a lowest price between 10 and 130, not where the hotel has a room with the lowest price between 10 and 130.
select
c.Category_name,
count(*) NumHotels
from
( select distinct
byRoomType.hotel_id
from
hotels_room_types_seasons bySeason
join hotels_room_types byRoomType
on bySeason.hotels_room_types_id = byRoomType.id_hotels_room_type
where
bySeason.Price between LowPriceParameter and HighPriceParameter
) QualifiedHotels
join Hotels
on QualifiedHotels.hotel_id = Hotels.id_hotel
join Categories c
on category_id = c.id_category