我有User,Play和UserPlay模型。这是User模型中定义的关系,用于计算总时间,用户玩过游戏。
'playedhours'=>array(self::STAT, 'Play', 'UserPlay(user_id,play_id)',
'select'=>'SUM(duration)'),
现在我正在尝试使用用户ID查找持续时间总和。
$playedHours = User::model()->findByPk($model->user_id)->playedhours)/3600;
这种关系需要花费大量时间来执行大量数据。然后查看关系生成的查询。
SELECT SUM(duration) AS `s`, `UserPlay`.`user_id` AS `c0` FROM `Play` `t` INNER JOIN
`UserPlay` ON (`t`.`id`=`UserPlay`.`play_id`) GROUP BY `UserPlay`.`user_id` HAVING
(`UserPlay`.`user_id`=9);
UserPlay.user_id上的GROUP BY花费了很多时间。因为我在这里不需要分组条款。
我的问题是,如何避免上述关系中的GROUP BY子句。
答案 0 :(得分:2)
根据定义,STAT关系是聚合查询,请参阅Statistical Query。
您无法在此处删除GROUP BY
并对汇总数据进行有意义的查询。 SUM(), AVG()
等所有聚合函数都参见GROUP BY Functions,以获取MYSQL支持的所有聚合函数的列表。
您的问题是针对您正在执行HAVING
子句的计算。这不是必需的,因为HAVING
在聚合发生后检查条件,您可以使用它来设置条件,例如SUM(duration) > 500
。
基本上发生的事情是您首先分别对所有用户进行分组,然后过滤您想要的用户ID。如果您改为使用WHERE子句来过滤之前 之后的,那么聚合仅适用于您想要的用户,然后将其分组,您的查询会更快。
虽然Active Record擅长以OOP方式建模数据,但它 实际上,由于它需要创建,性能会降低性能 一个或多个对象来表示查询结果的每一行。对于数据 密集型应用程序,使用较低级别的DAO或数据库API 可能是一个更好的选择
因此,最好将关系更改为直接使用CommandBuilder或DAO API查询Db的模型函数。像这样的东西
Class User extends CActiveRecord {
....
public function getPlayedhours(){
if(!isset($this->id)) // to prevent query running on a newly created object without a row loaded to it
return 0;
$played = Yii::app()->db->createCommand()
->select('SUM(duration)')
->from('play')
->join("user_play up","up.play_id = play.id")
->where("up.user_id =".$this->id)
->group("up.user_id")
->queryScalar();
if($played == null)
return 0;
else
return $played/3600 ;
}
....
}
如果查询仍然很慢,请尝试优化索引,实现缓存机制,并使用explain命令找出实际花费更多时间的内容,更重要的是为什么。如果没有什么是好的,请升级您的硬件。