如何通过一次数据库查询按评分统计评论数 无需服务端额外计算
完全可以,你可以直接通过数据库查询完成所有统计计算,无需拉取全量评论数据到服务端再处理,性能提升非常明显,以下是两种常用实现方案:
方案1:按评分分组统计
先通过分组查询拿到每个评分对应的评论数量,再自行取值计算:
// 查询返回以评分为键、对应评论数为值的集合 $ratingStats = Review::query() ->selectRaw('rating, COUNT(*) as count') ->where('product_id', $data['product_id']) ->whereNotNull('published_at') ->groupBy('rating') ->pluck('count', 'rating'); // 取值,无对应评分时默认返回0 $fiveStars = $ratingStats[5] ?? 0; $fourStars = $ratingStats[4] ?? 0; $threeStars = $ratingStats[3] ?? 0; $twoStars = $ratingStats[2] ?? 0; $oneStar = $ratingStats[1] ?? 0; // 也可以自行计算平均分,或者直接用方案2一步到位 $total = $fiveStars + $fourStars + $threeStars + $twoStars + $oneStar; $overallRating = $total ? ($fiveStars * 5 + $fourStars * 4 + $threeStars * 3 + $twoStars * 2 + $oneStar) / $total : 0;
方案2:一步到位全量统计(推荐)
直接通过聚合函数一次性查出所有需要的字段,连平均分都由数据库直接计算完成,全程只执行1次SQL查询,不需要服务端做任何额外计算:
$stats = Review::query() ->selectRaw( 'COUNT(CASE WHEN rating = 5 THEN 1 END) as five_stars, COUNT(CASE WHEN rating = 4 THEN 1 END) as four_stars, COUNT(CASE WHEN rating = 3 THEN 1 END) as three_stars, COUNT(CASE WHEN rating = 2 THEN 1 END) as two_stars, COUNT(CASE WHEN rating = 1 THEN 1 END) as one_star, AVG(rating) as overall_rating' ) ->where('product_id', $data['product_id']) ->whereNotNull('published_at') ->first(); // 直接取值即可,无评论时平均分返回null可自行设置默认值 $fiveStars = $stats->five_stars; $fourStars = $stats->four_stars; $threeStars = $stats->three_stars; $twoStars = $stats->two_stars; $oneStar = $stats->one_star; $overallRating = $stats->overall_rating ?? 0;
该方案兼容MySQL、PostgreSQL等绝大多数主流数据库,数据量越大相比你原写法的性能优势越明显,避免了全量拉取评论数据带来的内存占用和计算开销。
内容的提问来源于stack exchange,提问作者Sergey
相关产品推荐
相关产品推荐

