为什么Laravel/Eloquent不能将JOIN用于EagerLoad?

2022-08-30 14:38:47
<?php

class Cat extends Eloquent {

    public function user() {
        return $this->belongsTo('User');
    }
}

class User extends Eloquent {

    public function cats() {
        return $this->hasMany('Cat');
    }
}

现在:

$cats = Cat::with('user')->get();

执行 2 个查询:

select * from `cats`
select * from `users` where `users`.`id` in ('1', '2', 'x')

为什么它不能做:

select * from cats inner join users on cats.user_id = users.id

对于那些说表中有两个id列的人来说,使用别名可以很容易地避免:

select 
    c.id as cats__id,
    c.name as cats__name,
    c.user_id as cats__user_id,
    b.id as users__id,
    b.name as users__name
from cats c
inner join users b on b.id = c.user_id

更新

有人指出,Eloquent不知道模型中表的列,但我想他们可以提供一种在模型中定义它们的方法,这样它就可以使用别名并执行正确的连接,而不是额外的查询。


答案 1

我的猜测是,这允许预先加载多个一对多关系。例如,我们还有一个狗表:

class User extends Eloquent {

    public function cats() {
        return $this->hasMany('Cat');
    }

    public function dogs() {
        return $this->hasMany('Dog');
    }
}

现在,我们想要急切地将它们都与 User 一起加载:

$users = User::with('cats','dogs')->get();

没有联接可以将它们合并到单个查询中。但是,对每个“with”元素执行单独的查询确实有效:

select * from `users`
select * from `cats` where `user`.`id` in ('1', '2', 'x')
select * from `dogs` where `user`.`id` in ('1', '2', 'x') 

因此,虽然此方法在某些简单情况下可能会生成额外的查询,但它提供了在联接方法失败时预先加载更复杂数据的能力。

这是我对为什么会这样进行的猜测。


答案 2

我认为,当您想要使用LIMIT和/或OFFST时,联接查询方法有一个致命的缺点。

$users = User::with('cats')->get() - 这将输出以下 2 个查询。

select * from `users`
select * from `cats` where `user`.`id` in ('1', '2', 'x')

并且它不是一个单一的查询

select * from users inner join cats on cats.user_id = users.id

但是比方说,我们需要对这个记录集进行分页。

User::with('cats')->paginate(10) - 这将输出以下2个具有限制的查询。

select * from `users` limit 10
select * from `cats` where `user`.`id` in ('1', '2', 'x')

有了一个连接,它会像

select * from users inner join cats on cats.user_id = users.id limit 10

它将获取10条记录,但并不意味着10个用户,因为每个用户都可以拥有多个猫。

我认为另一个原因是,关系数据库和NOSQL数据库之间的关系可以通过分离的查询方法轻松实现。

同样,与前面的答案一样,id是模棱两可的,您必须在每个语句前面加上不需要的表名。

另一方面,JOIN比EXISTS昂贵,并且存在速度更快,因为它不命令RDBMS获取任何数据,只需检查相关行是否存在即可。EXISTS 用于返回布尔值,JOIN 返回整个其他表。

出于可扩展性目的,如果遵循分片架构,则必须删除JOIN。这是pinterest在扩展过程中练习的。http://highscalability.com/blog/2013/4/15/scaling-pinterest-from-0-to-10s-of-billions-of-page-views-a.html


推荐