如何从Laravel查询生成器获取数据库资源而不是数组


How to get database resource instead of array from Laravel Query Builder?

当我执行PDO语句时,内部存储了一个结果集,我可以使用->fetch()从结果中获取一行。

如果我想将整个结果转换为数组,我可以使用->fetchAll()

使用Laravel,在查询生成器文档中,我只有看到一种从执行查询中获得数组结果的方法。

// example query, ~30,000 rows
$scores = DB::table("highscores")
            ->select("player_id", "score")
            ->orderBy("score", "desc")
            ->get();
var_dump($scores);
// array of 30,000 items...
// unbelievable ...

是否有任何方法从查询生成器如PDO会返回的结果集?或者我是否被迫等待查询生成器在返回值之前构建整个数组?

可能是某种->lazyGet(),或->getCursor() ?


如果是这样的话,我不禁看到查询生成器是一个非常短视的工具。假设有一个查询选择了30,000行。使用PDO,我可以逐行,一次一个->fetch(),并以很少的额外内存消耗来处理数据。

另一方面,Laravel查询生成器?"内存管理,嗯?"这很好,只需将30,000行加载到一个大数组中!"


PS是的,我知道我可以使用->skip()->take()来抵消和限制结果集。在大多数情况下,这可以很好地工作,因为向用户展示30,000行甚至是不可用的。如果我想生成大型报告,我可以看到PHP很容易耗尽内存。

在@deczo指出一个未记录的函数->chunk()之后,我在源代码中挖了一下。我发现->chunk()是一个方便的包装器,可以将我的查询乘以几个查询,但会自动填充->step($m)->take($n)参数。如果我想构建我自己的迭代器,使用->chunk和我的数据集,我最终会在我的数据库上查询30,000次而不是1次。

这也没有真正的帮助,因为->chunk()接受一个回调,这迫使我在构建查询时耦合我的循环逻辑。即使函数是在其他地方定义的,查询也将发生在控制器中,它应该对我的视图或呈现器的复杂性不感兴趣。

进一步挖掘,我发现所有Query Builder查询都不可避免地通过'Illuminate'Database'Connection#run。

// https://github.com/laravel/framework/blob/3d1b38557afe0d09326d0b5a9ff6b5705bc67d29/src/Illuminate/Database/Connection.php#L262-L284
/**
 * Run a select statement against the database.
 *
 * @param  string  $query
 * @param  array   $bindings
 * @return array
 */
public function select($query, $bindings = array())
{
  return $this->run($query, $bindings, function($me, $query, $bindings)
  {
    if ($me->pretending()) return array();
    // For select statements, we'll simply execute the query and return an array
    // of the database result set. Each element in the array will be a single
    // row from the database table, and will either be an array or objects.
    $statement = $me->getReadPdo()->prepare($query);
    $statement->execute($me->prepareBindings($bindings));
    return $statement->fetchAll($me->getFetchMode());
  });
}

看到底部那个讨厌的$statement->fetchAll了吗?

这意味着数组用于所有人,总是和永远;你的愿望和梦想被抽象成一个无用的工具 Laravel Query Builder。

我无法表达我现在有多沮丧


我要说的是,Laravel的源代码至少组织得很好,格式也很好。现在让我们加入一些好的代码!

使用chunk:

DB::table('highscores')
   ->select(...)
   ->orderBy(...)
   ->chunk($rowsNumber, function ($portion) {
      foreach ($portion as $row) { // do whatever you like }
   });

显然返回的结果将与调用get相同,所以:

$portion; // array of stdObjects
// and for Eloquent models:
Model::chunk(100, function ($portion) {
    $portion; // Collection of Models
});

这是一种使用laravel查询生成器进行查询的方法,但是要使用底层的pdo fetch来循环遍历记录集,我相信这将解决您的问题-运行一个查询并循环记录集,这样您就不会在30k记录上耗尽内存。

这种方法将使用你在laravel中设置的所有配置内容,所以你不必单独配置pdo。

您还可以抽象出一个方法,使其易于使用,该方法接受查询构建器对象,并返回记录集(执行pdo语句),然后将其while循环,如下所示

$qb = DB::table("highscores")
        ->select("player_id", "score")
        ->orderBy("score", "desc");
$connection = $qb->getConnection();
$pdo = $connection->getPdo();
$query = $qb->toSql();
$bindings = $qb->getBindings();
$statement = $pdo->prepare($query);
$statement->execute($bindings);
while ($row = $statement->fetch($connection->getFetchMode()))
{
    // do stuff with $row
}