PDO"Uncaught exception'PDOException'..当其他未缓冲的查询处于活动状态时,无法执行查询.考虑使用PDOStatement :: fetchAll()."

Nat*_*ate 7 php mysql pdo exception

我知道这个问题已被多次询问,但我已经阅读了许多问题的答案,仍然无法理解为什么我收到此错误:

致命错误:带有消息'SQLSTATE [HY000]的未捕获异常'PDOException':常规错误:2014在其他未缓冲的查询处于活动状态时无法执行查询.考虑使用PDOStatement :: fetchAll().或者,如果您的代码只是针对mysql运行,则可以通过设置PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY属性来启用查询缓冲.

第一件事很奇怪,就是我在我的localhost(wampserver)上没有出错,但我确实在我的网络服务器上得到了它.我的localhost上的php版本是5.3.10,而我的web服务器上的php版本是5.3.13.

我已经读过,当前一个查询中的数据留在缓冲区中时,此错误的来源正在进行查询.对我来说情况并非如此 - 我已经回复了所有数据,而且我知道查询中返回的每一行都被提取.

话虽如此,我发现更改我的一个查询fetchAll而不是fetch修复问题,但它只是没有,因为我知道返回的所有行都被读取.当我用fetchAll为查询(它在环正在取得),I打印出阵列中的每个循环,并且只有一个项目是阵列,用于在循环的每个查询英寸

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

... code ...

query 1

... code ...

loop
query 2
end loop

... code ... 

query 3
Run Code Online (Sandbox Code Playgroud)

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

我还想补充一点,我已经尝试添加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);
Run Code Online (Sandbox Code Playgroud)

这是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();
    }
}
Run Code Online (Sandbox Code Playgroud)

谢谢你的帮助!

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

$row2 = $stmt->fetch(PDO::FETCH_ASSOC); var_dump($row2);
Run Code Online (Sandbox Code Playgroud)

输出是:

bool(false) 
Run Code Online (Sandbox Code Playgroud)

我偶然发现了PDO错误吗?

goa*_*oat 5

您需要进行提取,直到行提取尝试失败。我知道您的结果集中可能只有一行,并认为一次提取就足够了,但事实并非如此(当您使用无缓冲查询时)。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;
}
Run Code Online (Sandbox Code Playgroud)

一些数据库驱动程序可能有更有效的实现,不需要它们获取大量没人关心的行,但这是 PDO 的默认方式。反正...

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

鉴于这种:

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

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

$row = $stmt->fetch();
$stmt->closeCursor();
Run Code Online (Sandbox Code Playgroud)

或者

list($row) = $stmt->fetchAll(); //tricky
Run Code Online (Sandbox Code Playgroud)

或者

$row = $stmt->fetch();
while ($stmt->fetch()) {}
Run Code Online (Sandbox Code Playgroud)