雄辩的关系优化查询

时间:2017-06-13 07:33:49

标签: laravel laravel-5 eloquent

我有以下型号: 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');

   }
}

PricingPlan

class PricingPlan extends Model{

   public function limits(){

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

   }
}

PricingPlanLimits

我没有描述该模型,它不是问题的必要条件。但请记住,有一个名为 maxBoards 的属性。

问题是我只能使用 CardBoard 模型实例,我想从PricingPlanLImits获取 maxBoard 属性。所以我这样做了:

注意:我已经有了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运行更少的查询?

5 个答案:

答案 0 :(得分:0)

如果您使用with()方法,则可以在一个查询中获取数据。

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

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

答案 1 :(得分:0)

之前的评论与此解决方案并不太相关......

示例

$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();

答案 2 :(得分:0)

我通常按相反顺序行事:

$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'];

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

答案 3 :(得分:0)

您可以通过使用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连接,不能用雄辩的方式完成(但是你会错过所有的ORM乐趣)

答案 4 :(得分:0)

通常,通过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);

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