Причины ошибки MySQL 2014. Невозможно выполнить запросы, когда другие небуферизованные запросы активны.

Мой сервер работает с 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) который возвращает только одно значение.

Редактировать Кстати, именно так я создаю свое соединение. Я только недавно добавил 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');

12 ответов

Решение

Клиентский протокол 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.

Я надеюсь на лучший ответ, чем следующий. Хотя некоторые из этих решений могут "решить" проблему, они не отвечают на первоначальный вопрос о причинах этой ошибки.

  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 (вероятно, что я буду делать, если нет лучшего ответа)

У меня почти такая же проблема. Мой первый запрос после подключения к БД возвращает пустой результат и сбрасывает эту ошибку. Включение буфера не помогает.

Мой код подключения был:

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 не принуждается к истине. Это установлено по умолчанию.

Я также столкнулся с этой проблемой сегодня и заметил, что я поставил неправильный оператор SQL (SELECT) в PDO exec()метод. Тогда я пришел к выводу, что ставить можно только запись (INSERT, UPDATE, DELETE) Операторы SQL вместо чтения (SELECT) в метод.

!!! ПРЕДУПРЕЖДЕНИЕ !!!

Это также может произойти, если вы пытаетесь получить запрос, отличный от SELECT (например, UPDATE/INSERT/ALTER/CREATE)

У меня была та же проблема, я отправлял результаты в другой цикл функции. Быстрое решение состояло в том, чтобы сохранить все результаты в массиве (как сказал Билл, если он слишком велик, у вас есть другие проблемы, о которых нужно беспокоиться), после сбора данных я запустил отдельный цикл для вызова функции по одному.

Кроме того, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY не работал для меня.

если кто-то здесь, с ошибкой при создании таблиц,
это также происходит, если вы пытаетесь выполнить создание 2 таблиц в одном запросе;
эта ошибка была выдана, когда я запустил запрос ниже;

      $q24="create table if not exists table1 like template1;create table if not exists table2 like template2;";
$s24=$link->prepare($q24);
$s24->execute();

кажется, таблицы должны быть созданы отдельно;

      $q1="create table if not exists table1 like template1;";
$s1=$link->prepare($q1);
$s1->execute();
//and
$q2="create table if not exists table2 like template2;";
$s2=$link->prepare($q2);
$s2->execute();

Я получил эту ошибку, когда случайно дважды вызвал выполнение, один явно и один, который был скрыт в другом вызове (класс PDO). После удаления первого запуска ошибка исчезла.

У меня была такая же проблема, и я решил ее, удалив все первоначальные запросы, связанные с набором символов. поэтому я начал с

      $con = new \PDO(self::getDriver() . ":host=" . self::getHost() . ":".self::getPort()."; dbname=" . self::getName() . ";charset=utf8", self::getUser(), self::getPassword(), array( \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8;SET SESSION time_zone ='+01:00'"));

к

      $con = new \PDO(self::getDriver() . ":host=" . self::getHost() . ":".self::getPort()."; dbname=" . self::getName() . ";charset=utf8", self::getUser(), self::getPassword(), array( \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,\PDO::MYSQL_ATTR_INIT_COMMAND => "SET SESSION time_zone ='+01:00'"));

поэтому я удалил команду SET NAMES utf8;

  1. PDO::ATTR_PERSISTENT => trueв PDO создать время соединения(!) (Но я должен отключить setAttribute (PDO:: ATTR_STATEMENT_CLASS, ['DBStatement']);)
  2. $dbs->setAttribute (PDO:: MYSQL_ATTR_USE_BUFFERED_QUERY, истина);

Основная причина этой ошибки заключается в том, что MySQL пытается запустить « exec » вместо « execute » и наоборот.

Есть два оператора PDO, которые предназначены для выполнения запросов, и оба они не совпадают.

предназначен для выполнения команд и запросов, которые не создают набор результатов.

Пример: УСТАНОВИТЬ, ОБНОВИТЬ, ВСТАВИТЬ, УДАЛИТЬ и т. д.

предназначен для выполнения команд и запросов, которые производят набор результатов.

Например: ВЫБЕРИТЕ, ЗВОНИТЕ, ПОКАЖИТЕ, ОПТИМИЗИРУЙТЕ, ОБЪЯСНИТЕ и т. д.

Если вы используете эти команды в неправильном месте, вы получите эту ошибку.

Решение:

Будьте осторожны, где использовать и

В моем случае для Laravel я изменил свой запрос с

DB::select("УДАЛИТЬ ИЗ " . env('DB_PREFIX') . 'products WHEREproduct_id = ' . $product->id); // Внутренне Laravel будет работатьPDO::execute() метод «выбрать»

к

DB::table('product_currency')->where('product_id', $product->id)->delete(); // Внутренне Laravel будет работатьPDO::exec()

Надеюсь, это даст больше разъяснений!

Это может быть простая ошибка в строке SQL. У меня по ошибке было два SQL-запроса в одной переменной php. А ошибка "другие небуферизованные запросы активны" появлялась только при следующем запросе к БД, поэтому ее было сложно найти.

Другие вопросы по тегам