使用相同类型的多个关系改进急切加载

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】:

试试这些

首先替换您的foreignKeylocalKey

类消息扩展模型


    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 急切加载嵌套多个模型

EF急切加载和延迟加载的区别?

如何在 Laravel 中为 ORM 急切加载添加多个键约束?

.NET Core/EF Core 2.0 升级后急切加载“类型之间未定义强制运算符”

laravel eloquent - 在嵌套急切加载的关系上不使用