Laravel减少查询

时间:2019-01-21 13:21:00

标签: php mysql laravel eloquent

我使用Laravel Eloquent,并且我有以下代码:

<?php
$bulk = Bulk::where('id', 'bulkid1');
echo $bulk->info;
foreach($bulk->models as $model) {
  echo $model->info;
  $lastreport = $model->reports()->getQuery()->orderBy('created_at', 'desc')->first();
  echo $lastreport->message;
}    
?>

我要实现的是$lastreport已预加载。在这段代码中,查询将被执行太多次(每个批量有1000个模型,导致1000个子查询)。 在普通sql中,我可以这样做:

SELECT * FROM bulk
LEFT JOIN models ON bulk.id = models.bulk_id
LEFT JOIN ( SELECT *, MAX(created_at) AS created_at
   FROM
     reports
   GROUP BY
     model_id )
lastreport ON models.id = lastreport.model_id
WHERE bulk.id = 'bulkid1'

数据库伪代码:

TABLE bulks
 id, info

TABLE models
id, bulk_id, info

TABLE reports
id, model_id, message

1 个答案:

答案 0 :(得分:3)

这是N+1 selects problem这个问题的Laravel解决方案是eager loading

您可以这样做:

<?php
$bulk = Bulk::with(['models', 'models.reports' => function ($query) { 
      return $query->orderBy('created_at', 'desc');           
   } 
])->where('id', 'bulkid1')->first();
echo $bulk->info;
foreach($bulk->models as $model) {
  echo $model->info;
  $lastreport = $model->reports->first();
  echo $lastreport->message;
}    

这应确保(a)仅向所有模型加载一个附加查询,以及(b)向所有模型报告加载另一个附加查询。这样做的不利之处在于,由于orderBy子句无法真正表示为查询时间条件,因此加载的数据量超出了必要。