如何从Laravel Query Builder获取数据库资源而不是数组?

时间:2014-07-10 18:24:31

标签: php laravel query-builder

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

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

使用Laravel,在Query Builder docs中,我看到了一种从执行查询中获取数组结果的方法。

// 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会返回?或者我是否被迫等待Query Builder在返回值之前构建整个数组?

也许某种->lazyGet()->getCursor()


如果是这种情况,我会情不自禁地看到Query Builder是一个非常短视的工具。想象一下选择30,000行的查询。使用PDO,我可以逐行执行,一次->fetch(),并且只需很少的额外内存消耗即可处理数据。

另一方面,

Laravel Query Builder? “内存管理,嗯?没关系,只需将30,000行加载到一个大阵列中!”


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

3 个答案:

答案 0 :(得分:4)

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

这也没有用,因为->chunk()需要一个回调,迫使我在构建查询时耦合循环逻辑。即使函数是在其他地方定义的,查询也会在控制器中发生,控制器应该对我的View或Presenter的复杂性没什么兴趣。

进一步挖掘,我发现所有查询构建器查询都不可避免地通过\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源代码至少组织和格式化得很好。现在让我们在那里得到一些好的代码!

答案 1 :(得分:3)

使用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
});

答案 2 :(得分:2)

这是一种使用laravel查询构建器进行查询的方法,但是然后使用底层的pdo fetch来遍历记录集,我认为这将解决您的问题 - 运行一个查询并循环记录集,以便您不要在30k记录上耗尽内存。

此方法将使用您在laravel中设置的所有配置内容,因此您无需单独配置pdo。

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

$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
}