MySQL错误的原因2014无法在其他未缓冲的查询处于活动状态时执行查询

时间:2013-07-02 19:24:03

标签: php mysql pdo

我的服务器运行CentOS 6.4,使用带有CentOS的repos的yum安装了MySQL 5.1.69,并使用yum和ius的repos安装了PHP 5.4.16。 Edit3 升级到MySQL服务器版本:5.5.31由IUS社区项目分发,错误仍然存​​在。然后将库更改为mysqlnd,似乎消除了错误。尽管如此,需要知道为什么这个错误有时只会出现。

使用PDO并使用PDO::ATTR_EMULATE_PREPARES=>false创建PDO对象时,我有时会收到以下错误:

Table Name - zipcodes
Error in query:
SELECT id FROM cities WHERE name=? AND states_id=?
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
File Name: /var/www/initial_install/build_database.php
Line: 547
Time of Error: Tuesday July 2, 2013, 5:52:48 PDT

第547行是最后一行:

$stmt_check_county->execute(array($data[5],$data[4]));
if(!$county_id=$stmt_check_county->fetchColumn())
{
    $stmt_counties->execute(array($data[5]));
    $county_id=db::db()->lastInsertId();
}
//$stmt_check_county->closeCursor(); //This will fix the error
$stmt_check_city->execute(array($data[3],$data[4]));

几年前我遇到过类似的问题,但是从PHP 5.1升级到PHP 5.3(MySQL也可能更新了),问题神奇地消失了,现在我用PHP 5.5了。

为什么它只在PDO::ATTR_EMULATE_PREPARES=>false时出现,并且只有PHP的交替版本?

我还发现closeCursor()也会修复错误。是否应始终在每次SELECT查询后不使用fetchAll()时执行此操作?请注意,即使查询类似于SELECT COUNT(col2)只返回一个值,错误仍会发生。

编辑顺便说一句,这是我创建连接的方式。我最近才添加MYSQL_ATTR_USE_BUFFERED_QUERY=>true,但是,它无法解决错误。 此外,可以按原样使用以下脚本来创建错误。

function sql_error($e,$sql=NULL){return('<h1>Error in query:</h1><p>'.$sql.'</p><p>'.$e->getMessage().'</p><p>File Name: '.$e->getFile().' Line: '.$e->getLine().'</p>');}

class db {
    private static $instance = NULL;
    private function __construct() {}   //Make private
    private function __clone(){}   //Make private
    public static function db() //Get instance of DB
    {
        if (!self::$instance)
        {
            //try{self::$instance = new PDO("mysql:host=localhost;dbname=myDB;charset=utf8",'myUsername','myPassword',array(PDO::ATTR_EMULATE_PREPARES=>false,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC));}
            try{self::$instance = new PDO("mysql:host=localhost;dbname=myDB;charset=utf8",'myUsername','myPassword',array(PDO::ATTR_EMULATE_PREPARES=>false,PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC));}
            //try{self::$instance = new PDO("mysql:host=localhost;dbname=myDB;charset=utf8",'myUsername','myPassword',array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC));}
            catch(PDOException $e){echo(sql_error($e));}
        }
        return self::$instance;
    }
}

$row=array(
    'zipcodes_id'=>'55555',
    'cities_id'=>123
);
$data=array($row,$row,$row,$row);

$sql = 'CREATE TEMPORARY TABLE temp1(temp_id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (temp_id) )';
db::db()->exec($sql);

$sql='SELECT COUNT(*) AS valid FROM cities_has_zipcodes WHERE cities_id=? AND zipcodes_id=?';
$stmt1 = db::db()->prepare($sql);

$sql ='SELECT temp_id FROM temp1';
$stmt2 = db::db()->prepare($sql);

foreach($data AS $row)
{
    try
    {
        $stmt1->execute(array($row['zipcodes_id'],$row['cities_id']));
        $rs1 = $stmt1->fetch(PDO::FETCH_ASSOC);
        //$stmt1->closeCursor();
        syslog(LOG_INFO,'$rs1: '.print_r($rs1,1).' '.rand());
        $stmt2->execute();
        $rs2 = $stmt2->fetch(PDO::FETCH_ASSOC);
        syslog(LOG_INFO,'$rs2: '.print_r($rs2,1).' '.rand());
    }
    catch(PDOException $e){echo(sql_error($e));}            
}
echo('done');

8 个答案:

答案 0 :(得分:58)

MySQL客户端协议不允许多个查询“正在进行中”。也就是说,你已经执行了一个查询并且你已经获取了一些结果,但并非全部 - 然后你尝试执行第二个查询。如果第一个查询仍有要返回的行,则第二个查询会出错。

客户端库通过首次获取时隐式获取第一个查询的所有行来解决这个问题,然后后续提取只是迭代内部缓存的结果。这使他们有机会关闭光标(就MySQL服务器而言)。这是“缓冲查询”。这与使用fetchAll()的工作方式相同,因为两种情况都必须在PHP客户端中分配足够的内存来保存完整的结果集。

不同之处在于缓冲查询将结果保存在MySQL客户端库中,因此在按顺序获取()每行之前,PHP无法访问行。而fetchAll()会立即为所有结果填充PHP数组,允许您访问任何随机行。

使用fetchAll()的主要原因是结果可能太大而无法容纳在您的PHP memory_limit中。但是看起来您的查询结果只有一行,所以这应该不是问题。

您可以在获取最后一行之前closeCursor()以“放弃”结果。 MySQL服务器会收到通知,告知它可以在服务器端丢弃该结果,然后您可以执行另一个查询。在完成提取给定的结果集之前,不应该closeCursor()。

另外:我注意到你在循环中反复执行你的$ stmt2,但每次都会返回相同的结果。根据将循环不变代码移出循环的原则,您应该在启动循环之前执行一次,并将结果保存在PHP变量中。因此,无论使用缓冲查询还是fetchAll(),都不需要嵌套查询。

所以我建议用这种方式编写代码:

$sql ='SELECT temp_id FROM temp1';
$stmt2 = db::db()->prepare($sql);
$stmt2->execute();
$rs2 = $stmt2->fetchAll(PDO::FETCH_ASSOC);
$stmt2->closeCursor();

$sql='SELECT COUNT(*) AS valid FROM cities_has_zipcodes 
      WHERE cities_id=:cities_id AND zipcodes_id=:zipcodes_id';
$stmt1 = db::db()->prepare($sql);

foreach($data AS $row)
{
    try
    {
        $stmt1->execute($row);
        $rs1 = $stmt1->fetchAll(PDO::FETCH_ASSOC);
        $stmt1->closeCursor();
        syslog(LOG_INFO,'$rs1: '.print_r($rs1[0],1).' '.rand());
        syslog(LOG_INFO,'$rs2: '.print_r($rs2[0],1).' '.rand());
    }
    catch(PDOException $e){echo(sql_error($e));}            
}

注意我还使用了命名参数而不是位置参数,这使得将$ row作为参数值数组传递变得更加简单。如果数组的键与参数名称匹配,则只需传递数组即可。在旧版本的PHP中,您必须在数组键中包含:前缀,但不再需要它了。

无论如何你应该使用mysqlnd。它具有更多功能,内存效率更高,许可证与PHP兼容。

答案 1 :(得分:8)

我希望得到比以下更好的答案。虽然其中一些解决方案可能会“解决”问题,但他们并未回答有关导致此错误的原因的问题。

  1. 设置PDO::ATTR_EMULATE_PREPARES=>true(我不希望这样做)
  2. 设置PDO::MYSQL_ATTR_USE_BUFFERED_QUERY(对我不起作用)
  3. 使用PDOStatement::fetchAll()(并非总是可取)
  4. 在每个$stmt->closeCursor()之后使用$stmt->fetch()(这主要是有效的,但是,我仍有几个案例没有这样做)
  5. 将PHP MySQL库从php-mysql更改为php-mysqlnd(如果没有更好的答案,我可能会做什么)

答案 2 :(得分:6)

我几乎有同样的问题。连接到db后我的第一个查询返回空结果并删除此错误。启用缓冲区无济于事。

我的连接代码是:

try { 
    $DBH = new PDO("mysql:host=$hostname;dbname=$db_name", $username, $password, 
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET CHARACTER SET utf8; SET NAMES utf8",
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NUM));
} 
catch(PDOException $e) { echo $e->getMessage(); }

解决方法是删除初始命令:

PDO::MYSQL_ATTR_INIT_COMMAND => "SET CHARACTER SET utf8; SET NAMES utf8"

这是一个正确的代码:

try { 
    $DBH = new PDO("mysql:host=$hostname;dbname=$db_name", $username, $password, 
    array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NUM));
} 
catch(PDOException $e) { echo $e->getMessage(); }

并且MYSQL_ATTR_USE_BUFFERED_QUERY不会被强制为真。它被设置为默认值。

答案 3 :(得分:2)

<块引用>

!!! 警告!!!

如果您尝试获取非 SELECT 查询(例如 - UPDATE/INSERT/ALTER/CREATE),也会发生这种情况

答案 4 :(得分:1)

我遇到了同样的问题,我将结果发送到另一个函数mid循环。快速修复,将所有结果保存在一个数组中(比如Bill说,如果它太大,你还有其他问题需要担心),收集数据后,我运行一个单独的循环来一次调用一个函数。

另外,PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY对我不起作用。

答案 5 :(得分:0)

这可能是SQL字符串中的一个简单错误。我错误地在一个php变量中进行了两个SQL查询。而且错误“其他未缓冲的查询处于活动状态”仅在下一个数据库查询中出现,因此很难找到。

答案 6 :(得分:0)

我今天也遇到了这个问题,并注意到我在PDO的SELECT方法中放入了错误的SQL语句(exec())。然后我得出一个结论,我们只能将 write INSERTUPDATEDELETE)SQL语句放入 read ({SELECT)个方法。

答案 7 :(得分:-1)

    Pli中的
  1. SearchDelegate 创建连接时间(!)(但是我必须禁用setAttribute(PDO :: ATTR_STATEMENT_CLASS,['DBStatement']);)
  2. $ dbs-> setAttribute(PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY,true);