PDO “未捕获的异常 'PDOException' ..当其他无缓冲查询处于活动状态时,无法执行查询。请考虑使用 PDOStatement::fetchAll()。

PDO “Uncaught exception 'PDOException' .. Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll().”

提问人:Nate 提问时间:7/15/2012 最后编辑:Nate 更新时间:11/6/2021 访问量:11729

问:

我知道这个问题已经被问过很多次了,但我已经阅读了许多问题的答案,但仍然不明白为什么我会收到这个错误:

致命错误:未捕获的异常“PDOException”,并显示消息 “SQLSTATE[HY000]:常规错误:2014 无法执行查询,而 其他无缓冲查询处于活动状态。考虑使用 PDOStatement::fetchAll()。或者,如果您的代码只有 要针对 MySQL 运行,您可以通过设置 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY 属性。

奇怪的第一件事是,我没有在我的本地主机(wampserver)上收到错误,但我确实在我的Web服务器上得到了它。我的本地主机上的 php 版本是 5.3.10,而在我的 Web 服务器上是 5.3.13。

我读到这个错误的根源是在上一次查询的数据留在缓冲区中时进行查询。对我来说,情况并非如此——我已经回显了所有数据,并且我知道查询中返回的每一行都在被获取。

话虽如此,我发现将我的一个查询更改为而不是解决问题,但它根本没有,因为我知道返回的所有行都在被读取。当我用于查询时(它是在循环中进行的),我打印出每个循环的数组,并且循环中每个查询的数组中只有一个项目。fetchAllfetchfetchAll

还有一条信息。抛出 PDO 错误的不是我更改为的查询(使错误消失),我的 php 文件中稍后还有另一个查询抛出错误。我的文件基本上是这样的:fetchAll

... code ...

query 1

... code ...

loop
query 2
end loop

... code ... 

query 3

如果我注释掉查询 3,则没有错误。如果我注释掉,或更改为 ,查询 2,则没有错误。查询 1 没有任何影响。fetchAll

我还想补充一点,我已经尝试(同时)添加到页面上的所有查询中,但错误仍然存在。我认为这证明缓冲区中没有未读数据,对吧?LIMIT 1

我真的很困惑,所以我很感激你的建议。在有人问之前,我无法发布完整的代码,但这是我代码的简化版本:

$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
makeQuery($stmt, array(':par' => $var));
$row = $stmt->fetch(PDO::FETCH_ASSOC);


$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');

for loop
    makeQuery($stmt, array(':par' => $var));
    $row2 = $stmt->fetch(PDO::FETCH_ASSOC);
    ... [use row2] ...
end for loop


$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
makeQuery($stmt, array(':par' => $var));
$row3 = $stmt->fetch(PDO::FETCH_ASSOC);

这里是.makeQuery()

/**************************************************************************************************************
* Function: makeQuery                                                                                         *
* Desc: Makes a PDO query.                                                                                    *
* Pre conditions: The statement/query and an array of named parameters (may be empty) must be passed.         *
* Post conditions: The PDO query is executed. Exceptions are caught, displayed, and page execution stopped.   *
**************************************************************************************************************/
function makeQuery($stmt, $array, $errMsg = '')
{
    try 
    {
        $stmt->execute($array);
    }
    catch (PDOException $e) 
    {
        print $errMsg != ''?$errMsg:"Error!: " . $e->getMessage() . "<br/>";
        die();
    }
}

感谢您的帮助!

编辑:在查询 2 之后,我还尝试执行以下操作(因为这似乎是问题的根源:

$row2 = $stmt->fetch(PDO::FETCH_ASSOC); var_dump($row2);

输出为:

bool(false) 

我是否偶然发现了 PDO 错误?

php mysql 异常 pdo

评论


答:

5赞 goat 7/16/2012 #1

您需要提取,直到行提取尝试失败。我知道您可能在结果集中只有一行,并认为一次提取就足够了,但事实并非如此(当您使用无缓冲查询时)。PDO 不知道有多少行,直到它到达末尾,它试图获取下一行,但它失败了。

您可能还有其他语句没有完全“在提取失败之前提取”。是的,我看到你提取直到其中一个语句的提取失败,但这并不意味着你对所有语句都这样做了。

澄清一下—— 当您通过 execute() 执行查询时,您会创建一个结果集,该结果集必须从数据库获取到 php 中。PDO 一次只能处理其中 1 个“正在获取的结果集”(每个连接)。您需要完全获取结果集,一直到结果集的末尾,然后才能开始从对 execute() 的不同调用中获取不同的结果集。

当您“调用 fetch() 直到 fetch() 失败”时,当对 fetch() 的最后一次调用由于没有更多结果而失败时,PDO 会在内部记录您到达结果末尾的事实。然后,PDO 对结果被完全获取感到满意,并且它可以清理 php 和数据库之间为该结果集建立的任何内部资源,从而允许您进行/获取其他查询。

还有其他方法可以使 PDO “调用 fetch() 直到 fetch() 失败”。

  1. 只需使用 fetchAll(),它只会获取所有行,因此它将到达结果集的末尾。
  2. 或者直接调用 closeCursor()

*如果您查看 closeCursor() 的源代码,默认实现实际上只是获取行并丢弃它们,直到它到达末尾。它显然是用 c 语言编写的,但它或多或少是这样做的:

function closeCursor() {
    while ($row = $stmt->fetch()) {}
    $this->stmtFullyFetched = true;
}

某些数据库驱动程序可能具有更高效的实现,不需要它们获取大量没人关心的行,但这是 PDO 的默认方式。无论如何。。。

通常,使用缓冲查询时不会遇到这些问题。原因是因为对于缓冲查询,在执行它们之后,PDO会自动将db结果完全提取到php内存中,因此它会自动为您执行“调用fetch()直到fetch()失败”部分。当您稍后自己调用 fetch() 或 fetchAll() 时,它会从 php 内存中获取结果,而不是从数据库中获取结果。所以基本上,当使用缓冲查询时,结果集会立即被完全获取,所以没有机会同时有超过 1 个“正在获取的结果集”(因为 php 是单线程的,所以没有机会同时运行 2 个查询)。

鉴于此:

$sql = "select * from test.a limit 1";
$stmt = $dbh->prepare($sql);
$stmt->execute(array());

完全提取结果集的方法(假设您只需要第一行):

$row = $stmt->fetch();
$stmt->closeCursor();

list($row) = $stmt->fetchAll(); //tricky

$row = $stmt->fetch();
while ($stmt->fetch()) {}

评论

0赞 Nate 7/18/2012
只是为了确保我理解 - 即使只返回了一行,并且我知道对于一个事实,我需要多次获取?使用 closecursor() 比这样做更可取吗?谢谢。
0赞 Nate 7/23/2012
谢谢你的帮助..所以,为了确保我“明白”,你是否在每次调用 execute() 后调用 closecursor()?
2赞 Chris King 9/24/2013 #2

在为这个问题苦苦挣扎了几天后,我终于发现这对我有用:

$db = new PDO ($cnstring, $user, $pwd);
$db->setAttribute (PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);

评论

0赞 Alex Barker 1/24/2019
MYSQL_ATTR_USE_BUFFERED_QUERY并且实际上是相同的。我仍然不明白为什么在某些情况下返回后需要。fetchAll()$row = $stmt->fetch()$stmt->closeCursor();=== false
0赞 M_R_K 1/9/2021 #3

如果您尝试获取非 SELECT 查询(例如 - UPDATE/INSERT/ALTER/CREATE),也会发生这种情况。确保仅对 SELECT 查询使用 fetch 或 fetchAll。

可能的重复答案/问题