我正在使用Yii2的ActiveRecord实现(希望)完全按照应该使用的方式,根据文档。
在一个非常简单的设置中,表之间只有简单的关系,获取10个结果很快,100个很慢。 1000是不可能的。数据库非常小,索引完美。问题绝对是Yii2请求数据的方式,而不是数据库本身。
我正在使用标准的ActiveDataProvider,如:
$provider = new ActiveDataProvider([
'query' => Post::find(),
'pagination' => false // to get all records
]);
使用Yii2工具栏进行调试显示了一个简单请求的数千个单一SELECT,它应该只从表A获得50行,并将一些简单的“JOIN”连接到表B到表C.在普通SQL中,每个人都可以用一个SQL语句来解决这个问题。和两个连接。然而,Yii2为每一行中的每个关系激活一个SELECT(这对于保持ORM清洁是有意义的)。导致(或多或少)1 * 50 * 30 = 1500个查询,只获得每行的两个关系。
为什么Yii2使用这么多单一的SELECT,或者这是我的错误? 另外,有人知道如何“修复”这个吗?
由于这对我来说是一个非常重要的问题,我将在5月14日提供500赏金。
答案 0 :(得分:24)
默认情况下,Yii2使用延迟加载以获得更好的性能。这样做的结果是只有在访问它时才会获取任何关系,因此成千上万的sql查询。您需要使用预先加载。您可以使用\yii\db\ActiveQuery::with()
执行此操作:
指定应执行此查询的关系
假设您的关系为comments
,解决方案如下:
'query' => Post::find()->with('comments'),
从guide for Relations开始,with
将执行额外的查询以获得关系,即:
SELECT * FROM `post`;
SELECT * FROM `comment` WHERE `postid` IN (....);
要使用proper joining,请将eagerLoading
参数设置为true
而使用joinWith
代替:
此方法允许您重用现有关系定义来执行JOIN查询。根据指定关系的定义,该方法将一个或多个JOIN语句附加到当前查询。
所以
'query' => Post::find()->joinWith('comments', true);
将导致以下查询:
SELECT `post`.* FROM `post` LEFT JOIN `comment` comments ON post.`id` = comments.`post_id`;
SELECT * FROM `comment` WHERE `postid` IN (....);
来自@ laslov的评论和https://github.com/yiisoft/yii2/issues/2379
认识到使用
joinWith()
不会使用JOIN查询急切加载相关数据非常重要。由于各种原因,即使使用JOIN,仍将执行WHERE postid IN (...)
查询以处理急切加载。因此,您只应在特别需要JOIN时使用joinWith()
,例如过滤或订购相关表格的一列
<强> TLDR:强>
joinWith
= with
加上实际的JOIN(因此可以通过其中一个相关列过滤/订购/分组等)
答案 1 :(得分:3)
为了使用关系AR,建议为需要连接的表声明主外键约束。约束将有助于保持关系数据的一致性和完整性。
对外键约束的支持因DBMS而异。 SQLite 3.6.19或更早版本不支持外键约束,但您仍可以在创建表时声明约束。 MySQL的MyISAM引擎根本不支持外键。
在AR中,有四种类型的关系:
以下代码显示了我们如何声明User和Post类的关系。
class Post extends CActiveRecord
{
......
public function relations()
{
return array(
'author'=>array(self::BELONGS_TO, 'User', 'author_id'),
'categories'=>array(self::MANY_MANY, 'Category',
'tbl_post_category(post_id, category_id)'),
);
}
}
class User extends CActiveRecord
{
......
public function relations()
{
return array(
'posts'=>array(self::HAS_MANY, 'Post', 'author_id'),
'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
);
}
}
查询结果将作为相关AR类的实例保存到属性中。这被称为延迟加载方法,即,仅在最初访问相关对象时执行关系查询。以下示例显示了如何使用此方法:
// retrieve the post whose ID is 10
$post=Post::model()->findByPk(10);
// retrieve the post's author: a relational query will be performed here
$author=$post->author;
你在某种程度上做错了,请从文档http://www.yiiframework.com/doc/guide/1.1/en/database.arr
开始