Мой сервер запускает CentOS 6.4 с MySQL 5.1.69, установленным с помощью yum с репозиториями CentOS, и PHP 5.4.16, установленным с использованием yum с репозиториями ius. Edit3 Обновлено до версии MySQL Server: 5.5.31 Распространяется проектом сообщества IUS, и ошибка все еще существует. Затем изменили библиотеку на mysqlnd и, похоже, устранили ошибку. Тем не менее, с этим взад и вперед, нужно знать, почему эта ошибка иногда проявляется.
При использовании PDO и создании объекта PDO с использованием PDO::ATTR_EMULATE_PREPARES=>false
иногда возникает следующая ошибка:
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)
который возвращает только одно значение.
Edit Кстати, так я создаю свое соединение. Я только недавно добавил 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');
Клиентский протокол MySQL не позволяет выполнять более одного запроса. То есть вы выполнили запрос, и вы получили некоторые результаты, но не все, – тогда вы попытаетесь выполнить второй запрос. Если в первом запросе все еще есть строки для возврата, второй запрос получает ошибку.
Библиотеки клиентов обходятся вокруг этого, выбирая все строки первого запроса неявно при первой выборке, а затем последующие выборки просто перебирают результаты внутри кэширования. Это дает им возможность закрыть курсор (насколько это касается сервера MySQL). Это «буферизованный запрос». Это работает так же, как и с использованием fetchAll (), поскольку оба случая должны выделять достаточно памяти в PHP-клиенте для хранения полного набора результатов.
Разница в том, что буферизованный запрос содержит результат в клиентской библиотеке MySQL, поэтому PHP не может получить доступ к строкам, пока вы не выберете () каждую строку последовательно. В то время как fetchAll () сразу заполняет массив PHP для всех результатов, позволяя вам получить доступ к любой случайной строке.
Основная причина не использовать fetchAll () заключается в том, что результат может быть слишком большим, чтобы вписаться в ваш PHP memory_limit. Но, похоже, ваши результаты запроса имеют только одну строку, так что это не должно быть проблемой.
Вы можете закрытьCursor (), чтобы «отказаться» от результата, прежде чем вы извлекли последнюю строку. Сервер MySQL получает уведомление о том, что он может отменить этот результат на стороне сервера, а затем вы можете выполнить другой запрос. Вы не должны закрыватьCursor (), пока не закончите выбор заданного набора результатов.
Кроме того: я замечаю, что вы выполняете свой $ 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.
Я надеюсь на лучший ответ, чем на следующий. Хотя некоторые из этих решений могут «исправить» проблему, они не отвечают на исходный вопрос о том, что вызывает эту ошибку.
PDO::ATTR_EMULATE_PREPARES=>true
(я не хочу этого делать) PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
(не работает для меня) PDOStatement::fetchAll()
(не всегда желательно) $stmt->closeCursor()
после каждого $stmt->fetch()
(это в основном работало, однако у меня все еще было несколько случаев, когда это не так) У меня почти такая же проблема. Мой первый запрос после подключения к 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 не принуждается к true. Он установлен как значение по умолчанию.
У меня была та же проблема, я отправлял результаты в другую функцию mid loop. Быстрое исправление было, сохраните все результаты в массиве (например, Билл заявил, что если он слишком велик, у вас есть другие проблемы, о которых нужно беспокоиться), после сбора данных я запустил отдельный цикл для вызова функции по одному.
Кроме того, PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY не работал для меня.