Yii关系在查询中生成GROUP BY子句

时间:2014-07-16 06:44:49

标签: php mysql sql yii

我有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子句。

1 个答案:

答案 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命令找出实际花费更多时间的内容,更重要的是为什么。如果没有什么是好的,请升级您的硬件。