Yii框架2 ActiveRecord中如何使用带比较条件的关联预加载
问题根因
Yii框架的预加载(Eager Loading)逻辑中,关联定义里直接使用$this->属性获取当前模型字段值的写法是无效的。预加载时只会调用一次关联定义方法生成统一的关联查询SQL,此时$this指向的是临时创建的空模型实例或者首个模型实例,不会逐行匹配每个主记录的字段值,因此出现所有记录的条件都复用第一条属性的问题。
解决方案
方案1:联表查询关联(兼容原有关联定义,实现最简)
修改关联定义,将硬编码的$this->属性替换为SQL表达式引用主表字段,再使用joinWith做联表查询即可:
- 调整关联定义:
public function getNext() { return $this->hasOne(self::class, [ 'bd_name' => 'bd_name', 'uid_client' => 'uid_client', 'remind_uid_user' => 'remind_uid_user', ]) ->andOnCondition(['or', [">", 'next_tab.dt_remind', new \yii\db\Expression('main_tab.dt_remind')], ['and', ["=", 'next_tab.dt_remind', new \yii\db\Expression('main_tab.dt_remind')], [">", 'next_tab.tm_remind', new \yii\db\Expression('main_tab.tm_remind')] ] ]) ->orderBy(['next_tab.dt_remind' => SORT_ASC, 'next_tab.tm_remind' => SORT_ASC]); }
- 修改查询逻辑,指定表别名后联表查询:
$actions['index']['modifyQuery'] = function(CommentQuery $query) { $query->alias('main_tab')->joinWith('next next_tab'); };
注意:该方案为单SQL联表查询,适合中等数据量场景,数据量极大时建议使用方案2。
方案2:手动匹配下一条记录(全数据库兼容,性能最优)
不需要依赖Yii的关联预加载,一次性查出所有记录后在内存中分组匹配,仅执行1次SQL查询,性能远高于框架原生预加载,支持超大数据量:
// 第一步:查询时按分组键、时间排序 $actions['index']['modifyQuery'] = function(CommentQuery $query) { $query->orderBy([ 'bd_name' => SORT_ASC, 'uid_client' => SORT_ASC, 'remind_uid_user' => SORT_ASC, 'dt_remind' => SORT_ASC, 'tm_remind' => SORT_ASC ]); }; // 第二步:查询出结果后手动绑定next属性 $comments = Comment::find()->...->all(); $grouped = []; foreach ($comments as $comment) { // 按关联键分组 $key = implode('|', [$comment->bd_name, $comment->uid_client, $comment->remind_uid_user]); if (isset($grouped[$key])) { // 同组内上一条记录的next指向当前记录 end($grouped[$key])->next = $comment; } $grouped[$key][] = $comment; }
方案3:窗口函数预生成序号(数据库支持MySQL8.0+/MariaDB10.2+时可选)
通过窗口函数提前给同组评论生成排序序号,直接关联序号+1的记录即可,性能介于方案1和方案2之间,适合需要纯SQL实现的场景。
内容的提问来源于stack exchange,提问作者Konstantin Sh
相关产品推荐
相关产品推荐

