如何在Yii2中按课程评分总和排序Course模型查询?
解决Yii2中课程按评分总和排序及关联其他表时SUM值偏大的问题
首先,我们先实现基础的按评分总和排序的Yii2查询,再处理关联其他表时SUM值异常的核心问题。
一、基础实现:按评分总和排序
根据你提供的原生SQL,我们可以直接转换成Yii2的ActiveQuery写法,有两种常用方式:
方式1:直接使用leftJoin
$query = Course::find() ->alias("t") // 选择需要的字段,给评分总和设置别名方便排序 ->select([ 't.id', 't.title', // 根据你的course表实际字段调整 'SUM(rate_value.value) AS total_score' ]) ->where(["t.deleted" => 0]) ->leftJoin('rate', 'rate.course_id = t.id') ->leftJoin('rate_value', 'rate_value.rate_id = rate.id') // 按课程ID分组,确保每个课程的评分只统计一次 ->groupBy('t.id') // 按总评分降序排序 ->orderBy(['total_score' => SORT_DESC]);
方式2:利用模型关联(推荐,更符合Yii2 AR规范)
如果你的Course模型已经定义了rate关联,Rate模型定义了rateValues关联(比如hasMany(RateValue::class, ['rate_id' => 'id'])),可以用joinWith链式调用:
$query = Course::find() ->alias("t") ->select([ 't.id', 't.title', 'SUM(rate_value.value) AS total_score' ]) ->where(["t.deleted" => 0]) ->joinWith(['rate.rateValues' => function ($q) { $q->from('rate_value'); // 指定表名避免关联时的歧义 }]) ->groupBy('t.id') ->orderBy(['total_score' => SORT_DESC]);
二、解决关联其他表时SUM值偏大的问题
你提到关联其他表时SUM值会偏大,这是因为多表关联产生了笛卡尔积:比如当课程关联了分类、教师等其他表时,每条关联记录都会和rate、rate_value的记录相乘,导致评分被重复累加。
解决这个问题的核心思路是先通过子查询单独计算每个课程的总评分,再将子查询结果和主表、其他表关联,这样就不会出现重复计算的情况:
// 第一步:子查询单独计算每个课程的总评分,隔离其他表的影响 $scoreSubquery = RateValue::find() ->select([ 'rate.course_id', 'SUM(rate_value.value) AS total_score' ]) ->leftJoin('rate', 'rate.id = rate_value.rate_id') ->groupBy('rate.course_id'); // 第二步:主查询关联子查询和其他表,确保评分计算不受其他关联影响 $query = Course::find() ->alias("t") ->select([ 't.id', 't.title', // 用COALESCE处理无评分的课程,默认总分为0 'COALESCE(score.total_score, 0) AS total_score' ]) ->where(["t.deleted" => 0]) // 关联子查询,别名为score ->leftJoin(['score' => $scoreSubquery], 'score.course_id = t.id') // 这里可以安全关联其他表,比如课程分类表 ->leftJoin('course_category', 'course_category.course_id = t.id') // 按主表ID分组,确保结果唯一 ->groupBy('t.id') ->orderBy(['total_score' => SORT_DESC]);
这种方式先在子查询里独立计算评分总和,完全隔离了其他表的关联影响,不管后续关联多少表,总评分都会保持准确。
内容的提问来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

