【问题标题】:Eloquent Relationships Optimize QueryEloquent 关系优化查询
【发布时间】:2017-11-14 20:25:48
【问题描述】:

我有以下模型:CardBoard、User、UserPricingPlans、PricingPlanLimits

注意:不要介意模型代码是否有问题。它们工作正常。

纸板

class CardBoard extends Model{

   public function user(){

      return $this->belongsTo('Models\User','id_user');

   }
}

用户

class User extends Model{

   public function pricingPlans(){

      return $this->hasMany('Models\UserPricingPlan','id_user');

   }
}

定价计划

class PricingPlan extends Model{

   public function limits(){

      return $this->hasOne('Models\PricingPlanLimits','id_pricing_plan','id_pricing_plan');

   }
}

PricingPlanLimits

我不会描述那个模型,它不是问题所必需的。但请记住,有一个名为 ma​​xBoards 的属性。

问题是我只有 CardBoard 模型实例可以处理,我想从 PricingPlanLImits 获取 ma​​xBoard 属性。所以我这样做了:

注意:我这里已经有 CardBoard 模型实例了!

$maxBoard = $cardBoard->user->pricingPlans->last()->limits->maxBoard;

return $maxBoard;

上面的代码运行良好,但是这个操作生成的查询数量对我来说是一个开销。 Eloquent 为调用的 每个关系 执行 SELECT,我不想要所有这些数据和操作。

{
    "query": "select * from `users` where `users`.`id_user` = ? limit 1",
    "bindings": [
    ],
    "time": 0.96
}   
{
    "query": "select * from `users_princing_plan` where `users_princing_plan`.`id_user` = ? and `users_princing_plan`.`id_user` is not null",
    "bindings": [
    ],
    "time": 0.8
}
{
    "query": "select * from `pricing_plan_limits` where `pricing_plan_limits`.`id_pricing_plan` = ? and `pricing_plan_limits`.`id_pricing_plan` is not null limit 1",
    "bindings": [

    ],
    "time": 0.88
}

有没有办法优化这个并以 Eloquent-Way 运行更少的查询?

【问题讨论】:

  • 您好!为什么你的 SQL 语句执行时间是 0.96 秒? (似乎太长了)你有什么类型的数据库?您是否为列设置了索引?
  • 我已将原始日志替换为不显示任何重要内容。此查询耗时 0.64 秒,数据库使用 INNODB (MySQL) 作为引擎,并在自制的临时本地服务器上运行,仅用于测试目的。 id_user 是主键。
  • 如果服务器在 SSD 上,查询时间会更快。
  • 我认为你应该为你的模型添加额外的关系。对于PricingPlanLimits 添加pricingPlans()... 都错过了。然后就可以在这个相关模型上使用where 方法。 Querying Relations。您也可以只查询您需要的那些列。例如:User::get(['id', 'name'])
  • 可能我会使用 Eloquent 上的支点来准确表达我的情况,你有所有这些嵌套关系,你可以走一个“捷径”

标签: laravel laravel-5 eloquent


【解决方案1】:

如果你使用with()方法,你可以在一个查询中得到一个数据。

例如CardBoard::with('user.pricingPlans')->get();

因此可以使用with 方法优化您的查询。

【讨论】:

  • with 再次进行 CardBoard 模型查询并执行上述所有这些查询,实际上使情况变得更糟,因为它增加了一个查询...我已经有一个 cardBoard 模型实例,使用 load 会更好,因为它使用了已经创建的模型。
  • 我想要某种 INNER JOIN 来避免所有这些选择,并将 3 个查询减少到 2 个甚至 1 个,因为所有这些模型都有嵌套关系。
【解决方案2】:

以前的 cmets 与此解决方案不太相关...

示例

$cardboard->user()->whereHas('pricingPlans', function ($plans) {
    $plans->selectRaw('price_plan_limits.id, MAX(price_plan_limits.maxBoard) as MB'))
          ->from('price_plan_limits')
          ->where('price_plan_limits.id', 'price_plan.id')
          ->orderBy('MB', 'DESC')
})->get();

【讨论】:

  • 你能给我一个使用上述模型的例子吗?
  • 我会尝试实现它,但我更喜欢使用 Eloquent Pivot(自定义中间表模型)我认为解决方案会更加清晰。
  • 现在我们有一个没有解释的例子,请为孩子们考虑!
【解决方案3】:

我通常按相反的顺序去:

$maxBoard = PricingPlanLimits::whereHas(function($q)use($cardBoard){
    $q->whereHas('PricingPlan', function($q1)use($cardBoard){
        $q1->whereHas('User', function($q2)use($cardBoard){
            $q2->whereHas('CardBoard', function($q3)use($cardBoard){
                $q3->where('id', $cardBoard['id']);
            });
        });
        // Probably you have to improve this logic
        // It is meant to select select the last occurrence
        $q1->orderBy('created_at', 'desc');
        $q1->limit(1);
    });
})->first()['maxBoard'];

完全未经测试,但这应该是在一个查询中实现目标的正确方法。

【讨论】:

    【解决方案4】:

    您可能可以通过使用 hasManyThrough 关系来减少调用次数(请参阅:https://laravel.com/docs/5.4/eloquent-relationships#has-many-through)。

    在那种情况下,你会有类似的东西

    class CardBoard extends Model{
    
       public function userPricingPlans(){
    
          return $this->hasManyThrough('Models\UserPricingPlan', 'Models\User', 'id_user', 'id_user');
       }
    }
    

    然后你可以这样称呼它:

    $maxBoard = $cardBoard->userPricingPlans->last()->limits->maxBoard;
    

    要在一个查询中完成所有操作,您需要流畅且真实的 SQL 连接,而 eloquent 无法完成(但您会错过 ORM 的所有乐趣)

    【讨论】:

      【解决方案5】:

      通常情况下,用 3 次查询就可以得到结果,这样的查询通常需要 10 毫秒。但是您的每个查询都需要将近 1 秒,这太长了。不知道是什么原因。

      不过,您也可以通过单个查询获得相同的结果。

      你的命名有点不合常规。我使用更流行的命名约定,希望您可以应用到您的案例中。

      class CardBoard extends Model
      {
         protected $table = 'card_boards';
      
         public function user()
         {
            return $this->belongsTo(User::class,'user_id');
         }
      }
      
      class User extends Model
      {
         protected $table = 'users';
      
         public function pricingPlans()
         {
            return $this->hasMany(UserPricingPlan::class,'user_id');
         }
      }
      
      class PricingPlan extends Model
      {
         protected $table = 'pricing_plans';
      
      
         // This is a one to one relationship so I use a singular form.
         public function limit()
         {
            return $this->hasOne(PricingPlanLimit::class,'pricing_plan_id');
         }
      }
      
      class PricingPlanLimit extends Model
      {
          protected $table = "pricing_plan_limits";
      }
      

      得到结果的查询:

      $carboardId = 100;
      
      $latestPricingPlanSubQuery = PricingPlan::select('pricing_plans.*', DB::raw('MAX(created_at) as last_post_created_at'))
                         ->groupBy('user_id');
      
      $carboard = Carboard::select('card_boards.*', 'pricing_plan_limits.max_board')
          ->join('users', 'cardboards.user_id', '=', 'users.id')
          ->joinSub($latestPricingPlans, 'latest_pricing_plans', function ($join){
              $join->on('users.id', '=', 'latest_pricing_plans.user_id');
          })
          ->join('pricing_plan_limits', 'latest_pricing_plans.id', '=', 'pricing_plan_limits.pricing_plan_id')
      ->find($cardboardId);
      
      

      关键是要有一个子查询,只为每个用户获取最新的定价计划。

      【讨论】:

        猜你喜欢
        • 2018-07-25
        • 1970-01-01
        • 2018-05-27
        • 2021-07-22
        • 2020-12-17
        • 2019-08-24
        • 2013-11-30
        • 1970-01-01
        • 2017-10-07
        相关资源
        最近更新 更多