当我执行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容易耗尽内存。
答案 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
}