Reputation: 23
I'm trying to select a count of product reviews by rating, where the rating can be 0 - 5.
The following basic select works but won't give a count of ratings that don't exist in the underlying table.
SELECT Rating, COUNT(*) AS 'Reviews' FROM ProductReviews
WHERE ProductID = 'product1'
GROUP BY Rating
I've tried using a CTE to generate the missing results, joined to Reviews table using an outer join, as soon as I try to include the "group by" expression the results fall back to match the results from the basic query.
(I've checked that the CTE does indeed generate the full range of required values).
BEGIN
DECLARE @START AS INT = 0;
DECLARE @END AS INT = 5;
WITH CTE_Ratings AS
(
SELECT @START as cte_rating
UNION ALL
SELECT 1 + cte_rating
FROM CTE_Ratings
WHERE cte_rating < @END
)
SELECT
cte_rating AS 'ReviewRating'
, ISNULL(COUNT(*), 0) AS 'ReviewCount'
FROM CTE_Ratings
LEFT OUTER JOIN Reviews ON Reviews.Rating = cte_rating
WHERE ProductReviews.ProductID = 'product1'
AND cte_rating BETWEEN @START AND @END
GROUP BY cte_rating
END
(I also tried building a temporary table containing the required values, joined to the Reviews table, with identical results).
In the case of both of the above queries the results are:
Rating Reviews
0 1
3 3
4 9
5 47
Whereas what I'm trying to get to for the same data is:
Rating Reviews
0 1
1 0
2 0
3 3
4 9
5 47
Can anyone suggest when the addition of the Group By aggregate function is causing the query to fail, or how it might be improved ?
Upvotes: 2
Views: 2697
Reputation: 432210
The WHERE changes the OUTER JOIN to an INNER JOIN because you are filtering on optional rows from the outer table.
So, move the outer table filter into the JOIN
Also, you are already filtering between start and end in the CTE
WITH ...
-- CTE here
SELECT
C.cte_rating AS 'ReviewRating'
, ISNULL(COUNT(PR.Rating), 0) AS 'ReviewCount'
FROM
CTE_Ratings C
LEFT OUTER JOIN
ProductReviews PR ON C.cte_rating= PR.Rating AND PR.ProductID = 'product1'
GROUP BY
C.cte_rating
More clearly, you are actually doing this
WITH ...
-- CTE here
SELECT
C.cte_rating AS 'ReviewRating'
, ISNULL(COUNT(PR.Rating), 0) AS 'ReviewCount'
FROM
CTE_Ratings C
LEFT OUTER JOIN
(
SELECT PR.Rating
FROM ProductReviews
WHERE ProductID = 'product1'
) PR ON C.cte_rating= PR.Rating
GROUP BY
C.cte_rating
Upvotes: 2