重置PDO结果中的数组指针

25

我正在尝试从MySQL的SELECT语句转换到PDO语句,但遇到了问题。我想要对一个获取的数组进行两次迭代,两次都从第一行开始。在MySQL中,我会使用:

mysql_data_seek($result,0);

我使用PDO方法,但不确定如何完成相同的操作。下面的代码是我尝试这样做的方式。第一个while循环正常工作,但第二个while循环没有返回任何内容。

$pdo = new PDO('mysql:host=' . $host . ';dbname='.$database, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$stmt = $pdo->prepare('SELECT * FROM mytable WHERE active = 1 ORDER BY name ASC');
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->execute();

while($row = $stmt->fetch())
{
    //do something starting with row[0]
}
while($row = $stmt->fetch())
{
    //do something else starting with row[0]
}
7个回答

32

将结果保存到一个数组中,然后对该数组进行两次循环。

$pdo = new PDO('mysql:host=' . $host . ';dbname='.$database, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$stmt = $pdo->prepare('SELECT * FROM mytable WHERE active = 1 ORDER BY name ASC');
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->execute();

$rows = $stmt->fetchAll();

foreach ($rows as $r) {
    // first run
}

foreach ($rows as $r) {
    // seconds run
}

哇!那似乎很明显。不知道为什么我一直卡在使用while循环上。感谢你的提醒! - user1028866
因为您忽略了文档 - AnrDaemon
4
处理大量数据时,这不是一个好主意。所有数据都将存储在数组中。 - Aniket Singh

9

根据php手册,您可以多次发出查询。如果使用PDO::prepare()准备PDOStatement对象,则可以使用多个调用PDOStatement::execute()来发出语句。 因此,您的代码将如下所示:

$stmt = $pdo->prepare('SELECT * FROM mytable WHERE active = 1 ORDER BY name ASC');
$stmt->setFetchMode(PDO::FETCH_ASSOC);

//First execute
$stmt->execute();
while($row = $stmt->fetch())
{
    //do something starting with row[0]
}

//Second execute
$stmt->execute();
while($row = $stmt->fetch())
{
    //do something else starting with row[0]
}

source: http://php.net/manual/en/pdo.query.php


1
我给了你我的+1,这是正确的答案,我们不想fetchAll()几千行,尽管问题是“在PDO结果中重置数组指针”:这确实将游标重置为第一行,谢谢。 - Robert
只有在你确定正在处理SELECT时才是一个好主意。在我的情况下,我使用它来调试所有数据库查询,因此在调试模式下,INSERT语句总是会被重复插入,这让我花了一些时间才找出原因。 - Alexandre T.
@Robert 这个答案真的非常奇怪。如果你需要多次执行返回大量结果集的相同查询,那么你正在做一些非常奇怪的事情。你应该重新考虑你的方法,而不是采用如此暴力的方式。也许你需要在查询中添加 WHERE 或 LIMIT 子句来使它们不同。 - Your Common Sense
@YourCommonSense,这里的重点不同。这在问题和解决方案中都有自我解释。在我发表评论时,我遇到了类似的问题,被标记为“好”的答复并不是真正的“好”答复。WHERE子句存在,但重点是重置指针。 - Robert
@YourCommonSense 好的,那么修复被标记为好的回复,因为它没有起作用。我无法编辑我的原始评论,否则我会添加“使用小型数据库工作”,那是我的情况,不幸的是它们是三年前的,所以我甚至不知道他们是否修复了原始的答复。当时这个回复是有效的,而标记为好的回复却不是。 - Robert
@Robert,你难道不明白加入这样的条件会使你的方法无法扩展吗?而且当你选择一个合理的数据量时,被接受的答案可以完美地工作。但是如果你需要选择大量的数据,那么你应该重新考虑算法,以避免第二次传递。这将是正确的解决方案,而不是通过一遍又一遍地获取相同的数据来进行暴力操作。 - Your Common Sense

2
fetch — Fetches the next row from a result set

因此,当它退出第一个while循环时,它已经到达了您的resultSet的最后一个元素,这就是为什么第二个while返回空值。

使用fetchAll来存储所有结果,然后逐个遍历。


1
有时候无法存储 fetchAll() 的结果。相反,您可以在调用 fetchAll() 之前克隆 pdo 对象,就像这样。
$pdo_copy = clone $pdo;

$num_rows = count($pdo_copy->fetchAll());

现在我仍然可以使用pdo对象执行fetchObject()等语句;

1
致命错误:未捕获的错误:尝试克隆PDOStatement类的不可克隆对象。 - Stephen R

0

您需要通过在 prepare 中提供 PDO::CURSOR_SCROLL 选项来告诉语句它必须是可滚动的。然后,当到达末尾时,使用 PDO::FETCH_ORI_FIRST 再次回到第一个元素,以便您可以重新开始。

完整代码:

$pdo = new PDO('mysql:host=' . $host . ';dbname='.$database, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

//Making cursor scrollable
$options = array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL);
$stmt = $pdo->prepare('SELECT * FROM mytable WHERE active = 1 ORDER BY name ASC',
                $options);

$stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->execute();

while($row = $stmt->fetch())
{
    //do something starting with row[0]
}

//For the first row we use PDO::FETCH_ORI_FIRST 
//to get the first element again. This will also 
//move the cursor to that element
$first = true; 
while($row = $stmt->fetch(null, $first ? PDO::FETCH_ORI_FIRST : PDO::FETCH_ORI_NEXT)))
{
    $first = false;
    //do something else starting with row[0]
}

-2

另一个解决方案可能是如果您在结果查询字符串上再次运行查询并提取它:

public function fetch_again( $query_result, $fetch_type='fetch' )
{
    return $this->pdo->query($query_result->queryString)->$fetch_type();
}

-4
if($stmt->fetchColumn() >= 0)
{
    $stmt->execute(); //Reset cursor
    while($rs = $stmt->fetchObject())
    {
        echo "Data: ".$rs->data;
    }
}
else
{
    echo '0';
}

你能在回答的文本中解释一下吗? - Russia Must Remove Putin
OP想要对获取的数组进行两次迭代。我只能在你的代码中看到一个循环。那么,你的代码在多大程度上是解决OP问题的方案? - honk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接