为什么 Laravel 不自动优化模型查询?
Why Laravel does not optimize model queries automatically?
直到今天,我一直依赖 Laravel 关系,但自从我打开 mysql 日志后,我感到非常失望。
当我执行代码时
Company::with(['users', 'machines'])->get()
mysql.log
看起来是这样
select * from `company` where `company`.`id` = '48' limit 1
select * from `user` where `user`.`company_id` in ('48')
select * from `machine` where `machine`.`company_id` in ('48')
为什么 Laravel 不使用连接进行预取?此外,是否有任何方法可以提高性能并仍然使用 Laravel 模型?
我知道 Doctrine ORM 预加载通过使用连接非常好。
感谢您的帮助。
如果你真的想使用联接而不是 Eloquent 计算查询,我想你可以使用流畅的查询构建器(通过数据库外观 Laravel 附带)并坚持将该代码放入模型的方法中,以保持一切良好和 SRP。
例如:
class Company extends Model {
public function sqlWithJoin() {
$users = DB::table('company')
->leftJoin('user', 'company.id', '=', 'user.company_id')
->get();
return $users;
}
}
这将为您生成正确的连接查询。
至于为什么要这样做,您必须对这两个选项进行基准测试,看看哪个选项能为您的特定数据提供最佳性能。我不会一概而论,一个选项总是比另一个选项具有 better/worse 性能。
如评论中所述,我不确定为什么这是首选方法的性能方面,但从可用性来看,能够访问模型的关系,因为它是独立的 属性 比与 join
一起工作,尤其是在 many-to-one
关系的情况下。
让我们使用 ->with()
和 ->leftJoin()
方法比较上面的例子。
当使用 ->with()
时,每个关系都定义为 Company
的 属性,通过 $company->users
访问。 运行 一个 foreach()
循环这个 属性 foreach($company->users AS $user)
并输出信息,例如 username
、email
等,这很容易。此外,如果Company
没有 users
,您不必担心显示空值(对于使用 .
符号的链接模型尤其重要,例如 users.user_details
)。
现在,查看 leftJoin()
。如果您尝试在每个模型及其子模型上链接多个 leftJoins()
,您很可能得不到预期的结果。本质上,leftJoin()
处理 NULL
记录的能力不如单个查询。
接下来,要输出公司用户的列表,您必须 运行 一个循环,例如:
foreach($company AS $row){
echo $row->username;
echo $row->email;
// etc etc
}
这会产生问题,因为 Eloquent
根本无法很好地处理重复的属性。例如,如果公司和用户都有一个 email
字段,那么实际显示的是任何人的猜测。除非您执行 selectRaw("companies.email AS email, users.email AS user_email)"
,否则只会返回一个 email
属性。这也适用于像 id
这样的列,其中将使用 leftJoin()
获取多个列,但实际上只能访问一个。
长话短说,leftJoin()
在尝试连接多个表时可能会出现很多问题,可能会出现重复信息、空信息等。而 运行 的性能使用 ->with()
方法进行多个查询可能不是最好的,它可以更轻松地用于检索和显示信息。
直到今天,我一直依赖 Laravel 关系,但自从我打开 mysql 日志后,我感到非常失望。
当我执行代码时
Company::with(['users', 'machines'])->get()
mysql.log
看起来是这样
select * from `company` where `company`.`id` = '48' limit 1
select * from `user` where `user`.`company_id` in ('48')
select * from `machine` where `machine`.`company_id` in ('48')
为什么 Laravel 不使用连接进行预取?此外,是否有任何方法可以提高性能并仍然使用 Laravel 模型?
我知道 Doctrine ORM 预加载通过使用连接非常好。
感谢您的帮助。
如果你真的想使用联接而不是 Eloquent 计算查询,我想你可以使用流畅的查询构建器(通过数据库外观 Laravel 附带)并坚持将该代码放入模型的方法中,以保持一切良好和 SRP。
例如:
class Company extends Model {
public function sqlWithJoin() {
$users = DB::table('company')
->leftJoin('user', 'company.id', '=', 'user.company_id')
->get();
return $users;
}
}
这将为您生成正确的连接查询。
至于为什么要这样做,您必须对这两个选项进行基准测试,看看哪个选项能为您的特定数据提供最佳性能。我不会一概而论,一个选项总是比另一个选项具有 better/worse 性能。
如评论中所述,我不确定为什么这是首选方法的性能方面,但从可用性来看,能够访问模型的关系,因为它是独立的 属性 比与 join
一起工作,尤其是在 many-to-one
关系的情况下。
让我们使用 ->with()
和 ->leftJoin()
方法比较上面的例子。
当使用 ->with()
时,每个关系都定义为 Company
的 属性,通过 $company->users
访问。 运行 一个 foreach()
循环这个 属性 foreach($company->users AS $user)
并输出信息,例如 username
、email
等,这很容易。此外,如果Company
没有 users
,您不必担心显示空值(对于使用 .
符号的链接模型尤其重要,例如 users.user_details
)。
现在,查看 leftJoin()
。如果您尝试在每个模型及其子模型上链接多个 leftJoins()
,您很可能得不到预期的结果。本质上,leftJoin()
处理 NULL
记录的能力不如单个查询。
接下来,要输出公司用户的列表,您必须 运行 一个循环,例如:
foreach($company AS $row){
echo $row->username;
echo $row->email;
// etc etc
}
这会产生问题,因为 Eloquent
根本无法很好地处理重复的属性。例如,如果公司和用户都有一个 email
字段,那么实际显示的是任何人的猜测。除非您执行 selectRaw("companies.email AS email, users.email AS user_email)"
,否则只会返回一个 email
属性。这也适用于像 id
这样的列,其中将使用 leftJoin()
获取多个列,但实际上只能访问一个。
长话短说,leftJoin()
在尝试连接多个表时可能会出现很多问题,可能会出现重复信息、空信息等。而 运行 的性能使用 ->with()
方法进行多个查询可能不是最好的,它可以更轻松地用于检索和显示信息。