ThinkPHP中with()和withJoin()f方法预载入查询的一些区别

发布时间:2022-12-10浏览次数:868 次
with()方法查询为IN查询,如果想使用JOIN方式进行查询,则可以使用withJoin()方法来进行查询。with()方法默认的参数为:关联属性名,一般为字

在说ThinkPHP的关联预载入之前,首先需要明确一点,关联预载入仅在数据集时有效,对单条数据并无影响,如:

$article = Article::find(1);
$article->detail->content;

或:

$article = Article::with('detail')->find(1);
$article->detail->content;

实际的SQL语句都如下:

SELECT * FROM `think_article` WHERE `id` = 8 LIMIT 1
SELECT * FROM `think_article_detail` WHERE `aid` = 8 LIMIT 1

即:关联预载入对单条数据查询的性能等并无影响。

一、什么是N+1问题

以往我们要查询多条数据,然后通过循环的方式获取其关联属性的子属性时,会存在N+1的问题,如下:

$article = Article::select([1,2,3]);
foreach ($article as $key=>$value){
	echo $value->detail->content;
}

实际产生的SQL语句如下:

SELECT * FROM `think_article` WHERE `id` IN (1,2,3)
SELECT * FROM `think_article_detail` WHERE `aid` = 1 LIMIT 1
SELECT * FROM `think_article_detail` WHERE `aid` = 2 LIMIT 1
SELECT * FROM `think_article_detail` WHERE `aid` = 3 LIMIT 1

即,每一条数据的关联属性的子属性都需要一条SQL进行查询。查询3条数据,需要4条SQL查询。

但使用了with()预载入查询之后,则只会产生两条SQL语句,如下:

Article::with('detail')->select([1,2,3]);

实际SQL

SELECT * FROM `think_article` WHERE `id` IN (1,2,3)
SELECT * FROM `think_article_detail` WHERE `aid` IN (1,2,3)

使用with()查询将原来的SQL语句减少到2条,在数据量较大时,对于性能的提升很明显。

二、withJoin()方法查询

with()方法查询为IN查询,这点我们可以根据其SQL看出,如果想使用JOIN方式进行查询,则可以使用withJoin()方法来进行查询。

Article::withJoin('detail')->select([8,9,11]);

实际SQL如下:

SELECT `article`.`id`,`article`.`title`,`detail`.`id` AS `detail__id`,`detail`.`aid` AS `detail__aid`,`detail`.`content` AS `detail__content` FROM `think_article` `article` INNER JOIN `think_article_detail` `detail` ON `article`.`id`=`detail`.`aid` WHERE `article`.`id` IN ('8','9','11') 

即withJoin()通过使用JOIN方式的查询,只使用了一句SQL。withJoin()方法默认为INNER JOIN查询,如需修改为其他的查询方式,需添加第二个参数,如:withJoin('detail','left')。

三、with()方法参数

with()方法默认的参数为:关联属性名,一般为字符串,当对多个关联属性进行预载入时,或对关联模型进行条件约束时,需要使用数组方式,如:Article::with(['detail', 'info'])->select();

四、对关联模型进行约束

with()方法和withJoin()对关联模型进行约束时,可支持使用闭包的形式,此时参数需为数组形式,如:对关联属性的字段进行过滤:

Article::with(['detail'=>function($query){
	$query->field('id,aid,content');
}])->select([8,9,11]);

这里需要注意的是:with()方法可直接使用field()方法进行过滤,withJoin()方法需要使用withField()方法。

另外,withJoin()可以采用如下的简写形式:

withJoin(['detail' => ['id', 'aid', 'content']]) // 这种约束字段的简写形式仅对withJoin有效

with()方法虽然也支持同样的简写形式,如:

with(['detail' => ['id', 'aid', 'content']])

但代表的意思则完全不同,with()使用该写法,代表,要同时获取detail关联模型的id,aid和content子关联模型的数据,这点非常容易混淆,一定要注意。

当然对Filed字段进行过滤的话,可以在定义关联模型时通过增加条件的方式进行约束。

还需注意,在指定字段约束时,一定要包含关联字段。在指定约束时,都需使用withLimit()方法,且需对$query参数指定Relation约束,因为withLimit方法是关联类才有的方法。

五、with()方法和withJoin()方法的区别:

除了上面我们讲到的在进行字段约束时,witn()方法需要使用field()而withJoin()方法则需要使用withField()之外,with()和withJoin()还存在一些其他的区别。

with()方法获取到的数据,打印时,会展示关联数据,withJoin()方法的则不会。

扫一扫,在手机上查看