简体   繁体   English

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

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

When I execute a PDO statement, internally a result set is stored, and I can use ->fetch() to get a row from the result. 当我执行PDO语句时,在内部存储结果集,我可以使用->fetch()从结果中获取一行。

If I wanted to convert the entire result to an array, I could do ->fetchAll() . 如果我想将整个结果转换为数组,我可以->fetchAll()

With Laravel, in the Query Builder docs , I only see a way to get an array result from executing the query. 使用Laravel,在Query Builder文档中 ,我看到了一种从执行查询中获取数组结果的方法。

// 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 ...

Is there any way to get a result set from Query Builder like PDO would return? 有没有办法从查询生成器获取结果集,如PDO会返回? Or am I forced to wait for Query Builder to build an entire array before it returns a value ? 或者我是否被迫等待Query Builder在返回值之前构建整个数组?

Perhaps some sort of ->lazyGet() , or ->getCursor() ? 也许某种->lazyGet() ,或->getCursor()


If this is the case, I can't help but see Query Builder is an extremely short-sighted tool. 如果是这种情况,我会情不自禁地看到Query Builder是一个非常短视的工具。 Imagine a query that selects 30,000 rows. 想象一下选择30,000行的查询。 With PDO I can step through row by row, one ->fetch() at a time, and handle the data with very little additional memory consumption. 使用PDO,我可以逐行执行,一次->fetch() ,并且只需很少的额外内存消耗即可处理数据。

Laravel Query Builder on the other hand? 另一方面,Laravel Query Builder? "Memory management, huh? It's fine, just load 30,000 rows into one big array!" “内存管理,嗯?没关系,只需将30,000行加载到一个大阵列中!”


PS yes, I know I can use ->skip() and ->take() to offset and limit the result set. PS是的,我知道我可以使用->skip()->take()来偏移和限制结果集。 In most cases, this would work fine, as presenting a user with 30,000 rows is not even usable. 在大多数情况下,这样可以正常工作,因为向用户提供30,000行甚至无法使用。 If I want to generate large reports, I can see PHP running out of memory easily. 如果我想生成大型报告,我可以看到PHP容易耗尽内存。

After @deczo pointed out an undocumented function ->chunk() , I dug around in the source code a bit. 在@deczo指出一个未记录的函数->chunk() ,我在源代码中挖了一下。 What I found is that ->chunk() is a convenience wrapper around multiplying my query into several queries queries but automatically populating the ->step($m)->take($n) parameters. 我发现->chunk()是一个方便的包装器,可以将我的查询乘以多个查询查询,但会自动填充->step($m)->take($n)参数。 If I wanted to build my own iterator, using ->chunk with my data set, I'd end up with 30,000 queries on my DB instead of 1. 如果我想构建自己的迭代器,使用->chunk和我的数据集,我最终会在我的数据库上找到30,000个查询,而不是1。

This doesn't really help, too, because ->chunk() takes a callback which forces me to couple my looping logic at the time I'm building the query. 这也没有什么帮助,因为->chunk()接受一个回调,迫使我在构建查询时耦合我的循环逻辑。 Even if the function was defined somewhere else, the query is going to happen in the controller, which should have little interest in the intricacies of my View or Presenter. 即使函数是在其他地方定义的,查询也会在控制器中发生,这对我的View或Presenter的复杂性几乎没有兴趣。

Digging a little further, I found that all Query Builder queries inevitably pass through \\Illuminate\\Database\\Connection#run . 进一步挖掘,我发现所有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());
  });
}

See that nasty $statement->fetchAll near the bottom ? 看到底部附近讨厌的$statement->fetchAll

That means arrays for everyone, always and forever ; 这意味着每个人的阵列永远和永远 ; your wishes and dreams abstracted away into an unusable tool Laravel Query Builder. 你的愿望和梦想被抽象成一个无法使用的工具 Laravel Query Builder。

I can't express the valley of my depression right now. 我现在无法表达我抑郁的山谷。


One thing I will say though is that the Laravel source code was at least organized and formatted nicely. 我要说的一件事是Laravel源代码至少是有条理的组织和格式化的。 Now let's get some good code in there! 现在让我们在那里获得一些好的代码!

Use chunk : 使用chunk

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

Obviously returned result will be just the same as calling get , so: 显然返回的结果与调用get相同,所以:

$portion; // array of stdObjects

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

Here is a way to use the laravel query builder for making the query, but to then use the underlying pdo fetch to loop over the record set which I believe will solve your problem - running one query and looping the record set so you don't run out of memory on 30k records. 这是一种使用laravel查询构建器进行查询的方法,但是然后使用底层的pdo fetch来遍历记录集,我认为这将解决您的问题 - 运行一个查询并循环记录集,这样您就不会在30k记录上耗尽内存。

This approach will use all the config stuff you setup in laravel so you don't have to config pdo separately. 这种方法将使用您在laravel中设置的所有配置内容,因此您不必单独配置pdo。

You could also abstract out a method to make this easy to use that takes in the query builder object, and returns the record set (executed pdo statement), which you would then while loop over as below. 您还可以抽象出一个方法,使其易于使用,它接收查询构建器对象,并返回记录集(已执行的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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM