【问题标题】:Why Laravel does not optimize model queries automatically?为什么 Laravel 不会自动优化模型查询?
【发布时间】:2016-03-30 17:58:17
【问题描述】:

直到今天我还依赖 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 急切加载通过使用连接可以很好地工作。


感谢您的帮助。

【问题讨论】:

  • 请注意,您不需要多个->with() 子句,只需使用数组语法:->with(["users", "machines"])dot.notation 进行链接:->with(["users", "users.other"]) 等。
  • @TimLewis 谢谢,已修复。
  • 没问题 :) 我认为它不会以任何方式影响性能,但它使代码更易于阅读/维护。另外,好问题;我从来没有真正停下来检查过这样的日志,我知道为什么会这样做,但不知道这是否是最优化的。
  • @TimLewis 如果您觉得您的想法值得分享,请与我们的社区分享:)
  • 嗯,这不是一个真正的答案,但我想我可以提供一些见解

标签: mysql laravel doctrine query-builder rdbms


【解决方案1】:

如果你真的想使用连接而不是 Eloquent 计算查询,我想你可以使用流畅的查询构建器(通过 DB 外观随 Laravel 一起提供)并将该代码粘贴到模型的方法中以保持一切都很好,SRP。

例如:

class Company extends Model {
    public function sqlWithJoin() {
        $users = DB::table('company')
        ->leftJoin('user', 'company.id', '=', 'user.company_id')
        ->get();

        return $users;
    }
}

这将为您生成正确的连接查询。

至于为什么要这样做,您必须对这两个选项进行基准测试,看看哪一个可以为您的特定数据提供最佳性能。我不会一概而论,一种选择总是比另一种具有更好/更差的性能。

【讨论】:

  • 还值得记住的是,当您对越来越多的数据执行连接时,锁定表的可能性会越来越大 - 这会降低站点上其他用户的性能。对每个表执行多个选择查询是大多数 ActiveRecord 实现的工作方式。
  • @Dwight 感谢您做出如此重要的澄清。现在我有了一个调查的起点。
【解决方案2】:

正如 cmets 中所述,我不确定性能方面为什么这是首选方法,但从可用性来看,能够访问模型的关系,因为它拥有独立的属性,这比使用 join 容易得多,尤其是在many-to-one 关系的情况下。

让我们使用->with()->leftJoin() 方法比较上面的示例。

当使用->with() 时,每个关系都定义为Company 的属性,可通过$company->users 访问。很容易在这个属性foreach($company->users AS $user)上运行foreach()循环并输出信息,例如usernameemail等。另外,如果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() 方法运行多个查询的性能可能不是最好的,它可以更轻松地检索和显示信息。

【讨论】:

  • 感谢您如此详细的回答!值得一提的是,如果您想同时访问user.emailcompanies.email,可以在查询构建器上写->select('companies.email as company_email')。我相信 Laravel 以这种方式实现 QB 还有其他一些原因,因为使用连接仍然允许我们做与使用查询相同的事情。看看 Docrine ORM - 它使用连接并且仍然比 Eloquent ORM 更强大。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-24
  • 2012-03-20
  • 1970-01-01
  • 1970-01-01
  • 2018-05-27
  • 2021-07-22
相关资源
最近更新 更多