代码之家  ›  专栏  ›  技术社区  ›  sridesmet

Laravel减少查询

  •  1
  • sridesmet  · 技术社区  · 7 年前

    我使用Laravel口才,我有以下代码:

    <?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 回复  |  直到 7 年前
        1
  •  3
  •   apokryfos    7 年前

    这就是 N+1 selects problem 这个问题的解决办法是 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 不能真正表示为查询时间条件的子句。