【发布时间】:2013-06-30 07:45:28
【问题描述】:
我的服务器运行 CentOS 6.4 和 MySQL 5.1.69,使用 yum 和 CentOS 的 repos 安装,PHP 5.4.16 使用 yum 和 ius 的 repos 安装。 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');
【问题讨论】:
-
其他几篇文章。 stackoverflow.com/questions/12843886/…,stackoverflow.com/questions/3725346/…。两者都没有真正回答问题,而是暗示“在某些事情发生变化并且不再起作用之前不要担心它”。
-
对不起,我刚刚意识到我的 vps 创建了相同的服务器。它仍然应该在 PHP/MySQL 的交替版本上产生错误,或者当 php 不模拟存储过程时会有所不同。我会更新问题以反映新的理解。
-
我在您的问题中看不到它,但这个问题也存在(并且困扰)任何运行存储过程的人。您不能在存储过程的结果中运行查询,这会使事情变得非常难以解决。
-
不是一个完整的答案,但我遇到了同样的情况 - 通常的建议没有任何帮助 - 然后我在我的例程中删除了 CREATE TEMPORARY TABLE 的使用,突然问题就消失了。我相信它来自当事务处于活动状态时调用此例程(使用临时表)时。
-
人们往往会忘记 php 在垃圾清理方面的低效。在类似的情况下, cursor 和 gc_collect_cycles 为我做了。