我有一个由Yii生成的相当大且复杂的查询。为了生成这个查询,我们使用CDbCritera::with
来急切地加载多个相关的模型,并且我们使用多个作用域来限制返回的记录。生成的查询大约有700行长,但看起来像这样:
SELECT `t`.`column1` as `t0_c0`,
`t`.`column2` as `t0_c1`,
`related1`.`column1` as `t1_c0`,
...
`related9`.`column5` as `t9_c4`
FROM `model` `t`
LEFT OUTER JOIN `other_model` `related1`
ON ( `t`.`other_model_id` = `related1`.`id` )
...
LEFT OUTER JOIN `more_models` `related9`
ON ( `t`.`more_models_id` = `related9`.`id` )
WHERE
...big long WHERE clause using all of related1 - related9 to filter model...
LIMIT 10
我们的数据库有相当多的数据,但也不是不必要的。在本例中,model
表有大约126000行,每个“相关”模型都是BELONGS_TO
关系,并且t.XXX_id
上有一个索引,因此连接相当简单。问题是WHERE子句的复杂性。处理多个COALESCE
、IF
和CASE
子句。在126000行上执行过滤器需要2.6秒--比我们希望的API端点要长得多。
WHERE子句分为多个不同的部分,如下所示:
WHERE
( ... part 1 ... )
AND
( ... part 2 ... )
AND
( ... part 3 ... )
其中每个部件对应于范围之一,并且每个部件使用一个或多个相关模型
其中一个作用域只对***单个***相关模型进行过滤,并在此过程中将表从126000行过滤到大约2000行。我通过实验(在MySQL Workbench中)发现,只需执行以下操作,就可以将查询时间从2.6秒缩短到0.2秒:
SELECT `t`.`column1` as `t0_c0`,
`t`.`column2` as `t0_c1`,
`related1`.`column1` as `t1_c0`,
...
`related9`.`column5` as `t9_c4`
FROM
(
SELECT `model`.*
FROM `model`
LEFT OUTER JOIN `other_model`
ON ( `t`.`other_model_id` = `other_model`.`id` )
WHERE
( ... part 1 ... )
) `t`
LEFT OUTER JOIN `other_model` `related1`
ON ( `t`.`other_model_id` = `related1`.`id` )
...
LEFT OUTER JOIN `more_models` `related9`
ON ( `t`.`more_models_id` = `related9`.`id` )
WHERE
( ... part 2 ... )
AND
( ... part 3 ... )
LIMIT 10
通过这种方式,我们不必对原始model
表的所有126000行执行非常复杂的WHERE
子句,而是执行简单得多的(和索引增强的)WHERE
子句,然后只对2000个相关行执行复杂的WHERE
子句。但是在FROM
子句中使用子查询会使它的运行速度提高13倍。
我知道我可以使用CDbCommand
来构建一个查询,甚至传入原始SQL,但是我得到的是一个“行”数组--它们不会被Yii理解,也不会被正确地转换成正确的模型。
Yii的ActiveRecord系统是否有办法说出类似下面的话?
$criteria = new CDbCriteria;
$criteria->scopes = array("part1");
$subQuery = Model::model()->buildQuery($criteria);
$criteria = new CDbCriteria;
$criteria->scopes = array("part2", "part3");
$fullQuery = $subQuery->findAll($criteria);
1条答案
按热度按时间bgibtngc1#
虽然不是一个完美的解决方案,但我确实找到了***几乎***一样好的解决方案。
1.获取要在FROM子查询中选择的ID或模型
1.使用
id in (...)
将WHERE附加到外部查询如果有人感兴趣的话,我会找到我为这个问题写的代码,作为一个例子贴在答案里,但是到目前为止,这个问题得到的关注很少,一旦我找到了一个伪体面的解决方案,我就继续前进了。