在Postgres/PHP/PDO中迭代结果集的最佳实践


Best practice to iterate over result set in Postgres/PHP/PDO?

我使用PHP 5.3.6与PDO访问Postgres 9.0.4。我被要求减少报告的内存占用。当前的实现很简单:执行查询,执行fetchAll(),然后通过结果数组迭代foreach()。这显然不能扩展到巨大的结果集:它可以暂时消耗100MB或更多。

我有一个新的实现,它接受PDO语句句柄,然后使用foreach()直接迭代它,即通过fetchAll()没有中间数组。(据我所知,用foreach迭代语句句柄会在后台调用fetch()。)这同样快,并且消耗少得多的内存:大约28kB。尽管如此,我还是不相信我做得对,因为,尽管我已经做了大量的谷歌搜索,很难找到关于这个基本问题的答案:

  • 我看到一些文章建议使用光标来解决我最初的问题。Postgress PDO驱动程序已经在内部使用游标了吗?如果需要编写自己的SQL来创建游标,我愿意这样做,但我更愿意编写尽可能简单的代码(但不能更简单!)。

  • 如果foreach调用fetch()每次迭代,是不是太网络聊天?或者它是聪明的,一次取出许多行,例如500,以节省带宽?(这可能意味着它在内部使用游标。)

  • 我看过一篇文章,它将语句句柄包装在实现Iterator接口的类中。考虑到PDO语句句柄已经这样做了,这不是多余的吗?还是我错过了什么?

  • 我准备SQL语句的调用看起来像这样:

我发现这样做对内存和速度没有影响:

$sth = $dbh->prepare($sql, array( PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY ) );

这是因为这是Postgres PDO驱动程序的默认设置吗?如果它已经在内部使用游标,这将是有意义的。

欢迎对解决这个问题的方法和其他方法提出一般性意见。

PDO for Postgres内部使用游标

显然PDO::CURSOR_FWDONLY不使用游标。黑盒测试:

(0)准备:

$con = new 'PDO('dsn');
// you'll get "NO ACTIVE TRANSACTION" otherwise
$con->beginTransaction();
$sql = 'select * from largetable';

(1)默认值:

$stmt = $con->prepare($sql);
$stmt->execute();
print_r($stmt->fetch());

(2) FWDONLY - takes forever:

$stmt = $con->prepare($sql, array('PDO::ATTR_CURSOR => 'PDO::CURSOR_FWDONLY));
$stmt->execute();
print_r($stmt->fetch());

(3) SCROLLABLE -在flash中运行:

$stmt = $con->prepare($sql, array('PDO::ATTR_CURSOR => 'PDO::CURSOR_SCROLL));
$stmt->execute();
print_r($stmt->fetch());

我打开PG日志记录只是为了确定,它确实如此-只有SCROLL使用光标。

因此,使用游标的唯一方法是使用SCROLL,至少在PHP 5.4.23中是这样。