如何使用 Yii 1.1 在 FROM 子句中执行子查询?
How to perform subquery in FROM clause with Yii 1.1?
我有一个由 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
table 有大约 126000 行,每个 "related" 模型都是一个 BELONGS_TO
关系并且 t.XXX_id
上有一个索引,所以连接是公平的琐碎的。问题是 WHERE 子句的复杂性,拥有多个 COALESCE
和 IF
和 CASE
子句。在我们的 126000 行上执行过滤器需要 2.6 秒——比我们希望的 API 端点长得多。
WHERE 子句分为多个不同的部分,如下所示:
WHERE
( ... part 1 ... )
AND
( ... part 2 ... )
AND
( ... part 3 ... )
每个部分对应一个范围,每个部分使用一个或多个相关模型
其中一个范围仅在 单个 相关模型上进行过滤,并在这样做时将我们的 table 从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
table 的所有 126000 行执行非常复杂的 WHERE
子句,而是执行更简单(和索引增强)的 WHERE
对这 126000 行执行子句,然后仅对 2000 行相关行执行复杂的 WHERE
子句。这两个查询的结果是相同的,但是在 FROM
子句中使用子查询会使它 运行 快 13 倍。
问题是,我不知道如何在 Yii 中执行此操作。我知道我可以使用 CDbCommand
来构建查询,甚至可以传入原始的 SQL,但我得到的是一个 "rows" 的数组——它们不会被理解通过 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);
虽然不是完美的解决方案,但我确实找到了几乎的东西。将原始查询分成两部分:
- 在 FROM 子查询
中获取您希望 select 的 ID 或模型
- 使用
id in (...)
将 WHERE 附加到外部查询
如果有人感兴趣,我会在答案中找到我为此编写的代码 post 作为示例,但到目前为止,这个问题很少受到关注,一旦我发现了一个伪-不错的解决方案,我继续了。
我有一个由 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
table 有大约 126000 行,每个 "related" 模型都是一个 BELONGS_TO
关系并且 t.XXX_id
上有一个索引,所以连接是公平的琐碎的。问题是 WHERE 子句的复杂性,拥有多个 COALESCE
和 IF
和 CASE
子句。在我们的 126000 行上执行过滤器需要 2.6 秒——比我们希望的 API 端点长得多。
WHERE 子句分为多个不同的部分,如下所示:
WHERE
( ... part 1 ... )
AND
( ... part 2 ... )
AND
( ... part 3 ... )
每个部分对应一个范围,每个部分使用一个或多个相关模型
其中一个范围仅在 单个 相关模型上进行过滤,并在这样做时将我们的 table 从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
table 的所有 126000 行执行非常复杂的 WHERE
子句,而是执行更简单(和索引增强)的 WHERE
对这 126000 行执行子句,然后仅对 2000 行相关行执行复杂的 WHERE
子句。这两个查询的结果是相同的,但是在 FROM
子句中使用子查询会使它 运行 快 13 倍。
问题是,我不知道如何在 Yii 中执行此操作。我知道我可以使用 CDbCommand
来构建查询,甚至可以传入原始的 SQL,但我得到的是一个 "rows" 的数组——它们不会被理解通过 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);
虽然不是完美的解决方案,但我确实找到了几乎的东西。将原始查询分成两部分:
- 在 FROM 子查询 中获取您希望 select 的 ID 或模型
- 使用
id in (...)
将 WHERE 附加到外部查询
如果有人感兴趣,我会在答案中找到我为此编写的代码 post 作为示例,但到目前为止,这个问题很少受到关注,一旦我发现了一个伪-不错的解决方案,我继续了。