使用相同类型的多个关系改进急切加载
Posted
技术标签:
【中文标题】使用相同类型的多个关系改进急切加载【英文标题】:Improve eager loading with multiple relationships of the same type 【发布时间】:2021-06-21 05:27:10 【问题描述】:我有以下型号:
class Message extends Model
public function sender(): HasOne
return $this->hasOne(User::class, 'sender_id', 'id');
public function receiver(): HasOne
return $this->hasOne(User::class, 'receiver_id', 'id');
当我执行Message::with(['sender', 'receiver'])->all()
时,急切加载会执行以下查询:
SELECT * FROM messages
SELECT * FROM users IN(1, 3, 5)
SELECT * FROM users IN(3, 5, 7)
这几乎是最不冗余的方式。但它仍然加载用户 3 和用户 5 两次。有没有办法使用 eloquent 和 Eager 加载来进一步改善这一点?
【问题讨论】:
试试这个Message::with(['sender', 'receiver'])->groupby('sender.id','senders.id')->get();
@Droid 不起作用。
【参考方案1】:
不,不是。
您提供的示例是一个例外,没有任何迹象表明任何用户都会出现在发送方和接收方查询中。
您可以编写一些代码来更改 Eloquent 在这种情况下的行为方式,但这并不值得。
缺点:
案例仅限于在同一实体上急切加载的两个或多个关系。 您需要将删除的结果附加到第二个查询中,并使用正确的Order。 在代码中的性能可能比从数据库中保存的性能要高得多。【讨论】:
哦,好的。真的认为,它增加的开销比避免的多吗? @shaedrich 想象它像这样with(['sender' => function ($query) $query->orderBy('nick_name')->orderBy('first_name');])
现在您必须替换您删除到列表中的结果并遵守所需的顺序。您将需要 php 代码中的数据库逻辑 :)
恐怕我没明白你的意思。只要我在 with()
参数数组中没有附加到该关系的任何函数,查询应该是相同的。
@shaedrich 你是对的,但是在开发一个特性时,它需要对所有其他特性都有效,而不是例外。否则你最终会得到很多 if()
和一大堆要调试的东西。
你是对的。感谢您的深思熟虑(Y)【参考方案2】:
试试这些
首先替换您的foreignKey
和localKey
。
类消息扩展模型
public function sender(): HasOne
return $this->hasOne(User::class, 'sender_id', 'id');
public function receiver(): HasOne
return $this->hasOne(User::class, 'receiver_id', 'id');
然后使用这个模型查询
return Message::query()->with(['sender', 'receiver'])->get()
【讨论】:
感谢您的努力,但除了更正我的伪代码之外,它与我的代码完全相同。它不会改善急切加载。以上是关于使用相同类型的多个关系改进急切加载的主要内容,如果未能解决你的问题,请参考以下文章
EF CTP5 - 强类型急切加载 - 如何包含嵌套导航属性?
Laravel 混合获取 Eloquent 急切加载嵌套多个模型
如何在 Laravel 中为 ORM 急切加载添加多个键约束?