it-swarm.com.de

Ursachen des MySQL-Fehlers 2014 Es können keine Abfragen ausgeführt werden, während andere ungepufferte Abfragen aktiv sind

Auf meinem Server wird CentOS 6.4 mit MySQL 5.1.69 ausgeführt, das mit yum und den Repos von CentOS installiert wurde, und PHP 5.4.16, das mit yum und den Repos von ius installiert wurde. Edit3 Auf MySQL Server-Version aktualisiert: 5.5.31 Von The IUS Community Project verteilt, und der Fehler besteht weiterhin. Dann änderte Bibliothek zu mysqlnd und scheint, den Fehler zu beseitigen. Trotzdem muss man bei diesem Hin und Her wissen, warum sich dieser Fehler nur manchmal manifestiert.

Bei der Verwendung von PDO und dem Erstellen des PDO-Objekts mit PDO::ATTR_EMULATE_PREPARES=>false Wird manchmal die folgende Fehlermeldung angezeigt:

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

Zeile 547 ist die letzte Zeile von:

$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]));

Ich hatte vor einigen Jahren ein ähnliches Problem, aber ein Upgrade von PHP 5.1 auf PHP 5.3 (und MySQL wurde wahrscheinlich auch aktualisiert), und das Problem ist auf magische Weise verschwunden, und jetzt habe ich es es mit PHP 5.5.

Warum manifestiert es sich nur bei PDO::ATTR_EMULATE_PREPARES=>false Und nur mit alternierender Version von PHPs?

Ich habe auch festgestellt, dass closeCursor() auch den Fehler beheben wird. Sollte dies immer nach jeder SELECT Abfrage erfolgen, bei der fetchAll() nicht verwendet wird? Beachten Sie, dass der Fehler auch dann auftritt, wenn es sich bei der Abfrage um etwas wie SELECT COUNT(col2) handelt, das nur einen Wert zurückgibt.

Bearbeiten Übrigens, so erstelle ich meine Verbindung. Ich habe erst kürzlich MYSQL_ATTR_USE_BUFFERED_QUERY=>true Hinzugefügt, aber es heilt den Fehler nicht. Außerdem kann das folgende Skript so verwendet werden, wie es ist, um den Fehler zu erstellen.

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');
32
user1032531

Das MySQL-Client-Protokoll lässt nicht zu, dass mehrere Abfragen "in Bearbeitung" sind. Das heißt, Sie haben eine Abfrage ausgeführt und einige, aber nicht alle Ergebnisse abgerufen. Anschließend versuchen Sie, eine zweite Abfrage auszuführen. Wenn die erste Abfrage noch Zeilen enthält, die zurückgegeben werden müssen, wird bei der zweiten Abfrage ein Fehler gemeldet.

Client-Bibliotheken umgehen dies, indem sie all die Zeilen der ersten Abfrage implizit beim ersten Abruf abrufen, und nachfolgende Abrufe iterieren einfach über die intern zwischengespeicherten Ergebnisse. Dies gibt ihnen die Möglichkeit, den Cursor zu schließen (was den MySQL-Server betrifft). Dies ist die "gepufferte Abfrage". Dies funktioniert genauso wie die Verwendung von fetchAll (). In beiden Fällen muss im PHP client) genügend Speicher reserviert werden, um die gesamte Ergebnismenge aufzunehmen.

Der Unterschied besteht darin, dass eine gepufferte Abfrage das Ergebnis in der MySQL-Client-Bibliothek enthält, sodass PHP kann nicht auf die Zeilen zugreifen, bis Sie jede Zeile nacheinander abrufen (). Während fetchAll () sofort ein = auffüllt PHP Array für alle Ergebnisse, mit dem Sie auf jede beliebige Zeile zugreifen können.

Der Hauptgrund nicht für die Verwendung von fetchAll () ist, dass ein Ergebnis möglicherweise zu groß ist, um in Ihr PHP memory_limit) zu passen. Ihre Abfrageergebnisse haben jedoch nur eine Zeile sowieso, das sollte also kein problem sein.

Sie können CloseCursor () verwenden, um ein Ergebnis "abzubrechen", bevor Sie die letzte Zeile abgerufen haben. Der MySQL-Server wird benachrichtigt, dass er dieses Ergebnis auf der Serverseite verwerfen kann, und dann können Sie eine andere Abfrage ausführen. Sie sollten Cursor () erst schließen, wenn Sie mit dem Abrufen einer bestimmten Ergebnismenge fertig sind.

Außerdem: Ich stelle fest, dass Sie Ihr $ stmt2 immer wieder innerhalb der Schleife ausführen, aber es wird jedes Mal das gleiche Ergebnis zurückgeben. Nach dem Prinzip des Verschiebens von schleifeninvariantem Code aus der Schleife sollten Sie dies einmal ausgeführt haben, bevor Sie die Schleife starten, und das Ergebnis in einer PHP= Variable gespeichert haben. Unabhängig von der Verwendung von gepufferten Abfragen oder fetchAll (), Sie müssen Ihre Abfragen nicht verschachteln.

Daher würde ich empfehlen, Ihren Code folgendermaßen zu schreiben:

$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));}            
}

Hinweis: Ich habe auch benannte Parameter anstelle von Positionsparametern verwendet, wodurch es einfacher ist, $ row als Array von Parameterwerten zu übergeben. Wenn die Schlüssel des Arrays mit den Parameternamen übereinstimmen, können Sie das Array einfach übergeben. In älteren Versionen von PHP) mussten Sie das : Präfix in den Array-Schlüsseln, aber das brauchen Sie nicht mehr.

Sie sollten trotzdem mysqlnd verwenden. Es verfügt über mehr Funktionen, ist speichereffizienter und die Lizenz ist mit PHP kompatibel.

60
Bill Karwin

Ich hoffe auf eine bessere Antwort als die folgenden. Während einige dieser Lösungen das Problem möglicherweise "beheben", beantworten sie nicht die ursprüngliche Frage, was diesen Fehler verursacht.

  1. Setze PDO::ATTR_EMULATE_PREPARES=>true (Ich möchte das nicht tun)
  2. Setze PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (Hat bei mir nicht funktioniert)
  3. Benutze PDOStatement::fetchAll() (nicht immer wünschenswert)
  4. Benutze $stmt->closeCursor() nach jedem $stmt->fetch() (das hat meistens geklappt, aber ich hatte immer noch einige Fälle, in denen es nicht geklappt hat)
  5. Ändere PHP MySQL Bibliothek von php-mysql zu php-mysqlnd (wahrscheinlich was ich tun werde, wenn ich keine bessere Antwort bekomme)
8
user1032531

Ich habe fast das gleiche Problem. Meine erste Abfrage nach der Verbindung zu DB gibt leeres Ergebnis zurück und löscht diesen Fehler. Das Aktivieren des Puffers hilft nicht.

Mein Verbindungscode war:

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(); }

Meine Lösung bestand darin, den anfänglichen Befehl zu entfernen:

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

Hier ist ein korrekter Code:

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(); }

Und MYSQL_ATTR_USE_BUFFERED_QUERY wird nicht zu true gezwungen. Es ist als Standard festgelegt.

6
oleg

Ich hatte das gleiche Problem, ich habe Ergebnisse an eine andere Funktion in der Zwischenschleife gesendet. Schnelle Lösung war, alle Ergebnisse in einem Array zu speichern (wie Bill sagte, wenn es zu groß ist, haben Sie andere Probleme zu befürchten), nach dem Sammeln der Daten habe ich eine separate Schleife ausgeführt, um die Funktion einzeln aufzurufen.

Außerdem hat PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY bei mir nicht funktioniert.

1
tand3m