2017-06-13 90 views
7

我有以下型号:纸板,用户,UserPricingPlans,PricingPlanLimits雄辩关系优化查询

注:,如果有什么不对的车型code.They工作正常,不要介意。

纸板

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

的问题是,我只有纸板模型实例去努力,我想从PricingPlanLImits的maxBoard属性。所以我这样做:

注:我已经在这里CardBoard模型实例!上述

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

return $maxBoard; 

的代码运行很好,但这种操作生成的查询次数为开销我。口才做选择每个关系调用,我不希望所有这些数据和操作。

{ 
    "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 
} 

是不是有一种方法来优化这一点,并在一个雄辩的方式运行更少的查询?

+0

你好!为什么你的SQL语句在0.96秒内执行? (它似乎太长)你有什么类型的数据库?你是否为你的专栏设置了索引? – YanDatsyuk

+0

我已经取代原来的日志,不显示任何重要的东西。 这个查询需要0.64秒,数据库使用INNODB(MySQL)作为引擎,并在自制即兴本地服务器上运行,仅用于测试目的。 id_user是主键。 –

+0

如果服务器在SSD上,查询时间会更快。 –

回答

0

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

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

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

+0

** with **使CardBoard模型查询再次并执行上述所有这些查询,实际上使情况变得更糟,因为它增加了一个查询... 我已经有一个cardBoard模型的实例,使用** load **会因为它使用了一个已经创建好的模型,所以效果更好。 –

+0

我想要某种INNER JOIN来避免所有这些选择,并将3个查询减少到2个甚至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(); 
+0

你能给我一个使用上述模型的例子吗? –

+0

查看编辑我的答案 – btl

+0

我会尝试实现,但我更喜欢使用Eloquent Pivot(自定义中间表模型)我认为解决方案将更加清晰。 –

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

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

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的乐趣)