2013-07-02 8 views
26

Il mio server esegue CentOS 6.4 con MySQL 5.1.69 installato usando yum con reposos di CentOS e PHP 5.4.16 installato usando yum con i repos di ius. Edit3 Aggiornato alla versione MySQL Server: 5.5.31 Distribuito da Progetto comunità IUS e l'errore esiste ancora. Quindi cambia libreria in mysqlnd e sembra eliminare l'errore. Tuttavia, con questo avanti e indietro, è necessario sapere perché questo errore si manifesta solo a volte.Cause di errore MySQL 2014 Impossibile eseguire query mentre sono attive altre query non bufferizzate

Quando si utilizzano prodotti DOP e creazione dell'oggetto PDO utilizzando PDO::ATTR_EMULATE_PREPARES=>false, a volte ottengo il seguente errore:

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 

linea 547 è l'ultima linea di:

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

Ho avuto un problema simile diversi anni fa, ma ho aggiornato da PHP 5.1 a PHP 5.3 (e probabilmente anche MySQL è stato aggiornato), e il problema è scomparso magicamente, e ora lo ho con PHP 5.5.

Perché si manifesta solo quando PDO::ATTR_EMULATE_PREPARES=>false e con la sola versione alternativa di PHP?

Ho anche trovato che closeCursor() correggerà anche l'errore. Questo dovrebbe sempre essere eseguito dopo ogni query SELECT in cui non è utilizzato fetchAll()? Notare che l'errore si verifica ancora anche se la query è qualcosa come SELECT COUNT(col2) che restituisce solo un valore.

Modifica A proposito, questo è il modo in cui creo la mia connessione. Ho solo recentemente aggiunto MYSQL_ATTR_USE_BUFFERED_QUERY=>true, tuttavia, non cura l'errore. Inoltre, è possibile utilizzare il seguente script per creare l'errore.

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'); 
+0

Un paio di altri post. http://stackoverflow.com/questions/12843886/when-should-i-use-closecursor-for-pdo-statements, http://stackoverflow.com/questions/3725346/pdo-bindparam-pdo-bindvalue-and- DOP-closecursor.Neanche risponde alla domanda, ma implica "non ti preoccupare finché non cambia qualcosa e le cose non funzionano più". – user1032531

+0

Mi spiace, ho appena realizzato che il mio vps crea lo stesso server. Ancora dovrebbe creare un errore nelle versioni alternative di PHP/MySQL, o differire quando php non sta emulando le stored procedure. Aggiornerò la domanda per riflettere una nuova comprensione. – user1032531

+0

Non lo vedo nella tua domanda, ma questo è un problema che esiste anche (e perseguita) chiunque esegua stored procedure. Non è possibile eseguire query all'interno dei risultati di una stored procedure che può rendere le cose abbastanza difficili da aggirare. – JM4

risposta

44

Il protocollo del client MySQL non consente più di una query "in corso". Cioè, hai eseguito una query e hai recuperato alcuni dei risultati, ma non tutti - quindi provi a eseguire una seconda query. Se la prima query ha ancora righe da restituire, la seconda query riceve un errore.

librerie client aggirare questo recupero tutti le righe della prima query implicitamente su prima recuperano, e poi recupera successive è sufficiente scorrere i risultati memorizzati nella cache internamente. Questo dà loro la possibilità di chiudere il cursore (per quanto riguarda il server MySQL). Questa è la "query bufferizzata."Funziona allo stesso modo dell'utilizzo di fetchAll(), in quanto entrambi i casi devono allocare memoria sufficiente nel client PHP per contenere il set completo di risultati

La differenza è che una query memorizzata nel buffer contiene il risultato nella libreria client MySQL, così PHP non può accedere alle righe fino fetch() ciascuna riga in sequenza. considerando fetchAll() popola immediatamente un array PHP per tutti i risultati, consentendo di accedere ogni riga casuale.

La ragione principale non usare fetchAll() è che un risultato potrebbe essere troppo grande per rientrare nel tuo memory_limit PHP, ma sembra che i risultati della tua query abbiano comunque solo una riga, quindi non dovrebbe essere un problema.

Puoi chiudereCursore() per "abbandonare" un risultato prima di recuperare l'ultima riga. Il server MySQL viene avvisato che può scartare quel risultato sul lato server e quindi è possibile eseguire un'altra query. Non devi chiudereCursore() finché non hai finito di recuperare un determinato set di risultati.

Inoltre: noto che stai eseguendo più e più volte $ stmt2 all'interno del ciclo, ma ogni volta restituirà lo stesso risultato. Sul principio di spostare il codice loop-invariante dal ciclo, dovresti averlo eseguito una volta prima di iniziare il ciclo e salvato il risultato in una variabile PHP. Quindi, indipendentemente dall'utilizzo di query memorizzate nel buffer o da fetchAll(), non è necessario per nidificare le query.

quindi consiglierei di scrivere il codice in questo modo:

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

Nota Ho anche usato parametri denominati invece di parametri posizionali, che rende più semplice per passare $ riga come la matrice dei valori dei parametri. Se le chiavi dell'array corrispondono ai nomi dei parametri, puoi semplicemente passare l'array. Nelle versioni precedenti di PHP è necessario includere il prefisso : nelle chiavi dell'array, ma non è più necessario.

Si dovrebbe comunque usare mysqlnd. Ha più funzioni, è più efficiente in termini di memoria e la sua licenza è compatibile con PHP.

+0

È '$ stmt1-> closeCursor();' e '$ stmt2-> closeCursor();' ridondante dato che stai usando 'fetchAll()'? – user1032531

+2

Non penso che closeCursor() sia ridondante. La funzione fetchAll() non chiude i cursori e pertanto non libera le risorse lato server. –

+1

Mi ha aiutato molto, grazie! –

2

Sto sperando in una risposta migliore rispetto al seguente. Mentre alcune di queste soluzioni potrebbero "risolvere" il problema, non rispondono alla domanda originale su cosa causa questo errore.

  1. Set PDO::ATTR_EMULATE_PREPARES=>true (non voglio fare questo)
  2. Set PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (non ha funzionato per me)
  3. Usa PDOStatement::fetchAll() (non sempre auspicabile)
  4. Usa $stmt->closeCursor() dopo ogni $stmt->fetch() (questo ha funzionato per lo più, tuttavia, ho ancora avuto diversi casi in cui non funzionava)
  5. Cambia la libreria PHP MySQL da php-mysql a php-mysqlnd (probabilmente quello che farò se non ci sarà una risposta migliore)
+1

Inoltre: se stai eseguendo una query che restituisce almeno una riga usando 'PDO :: exec()', non c'è modo di chiudere il cursore e qualsiasi successiva query avrà esito negativo. Non usare 'PDO :: exec()', usa 'PDO :: query()' seguito da '$ st-> closeCursor()'. – rustyx

+0

@rustyx grazie per quel commento, è stato esattamente ciò che stava causando il mio problema! Stavo eseguendo gli aggiornamenti del database in un loop usando exec() perché non avevo bisogno dei dati restituiti (e per la maggior parte delle query non ce n'erano), ma c'erano query fittizie come SELECT DATABASE() che lanciavano l'eccezione all'esecuzione del prossima query, rendendo questo problema molto difficile da trovare e risolvere. – jurchiks

+1

Usa $ stmt-> closeCursor() dopo ogni $ stmt-> fetch() ha funzionato per me. – Jam

1

Ho avuto lo stesso problema, stavo inviando i risultati a un'altra funzione mid loop. La correzione rapida era, salvare tutti i risultati in un array (come indicato da Bill, se è troppo grande ci sono altri problemi di cui preoccuparsi), dopo aver raccolto i dati, ho eseguito un ciclo separato per chiamare la funzione uno alla volta.

Inoltre, PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY non ha funzionato per me.

2

Ho quasi lo stesso problema. La mia prima query dopo la connessione a db restituisce un risultato vuoto e rilascia questo errore. L'attivazione del buffer non aiuta.

Il mio codice di collegamento era:

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

soluzione nel mio modo era quello di eliminare il comando iniziale:

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

Ecco un codice corretto:

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

E MYSQL_ATTR_USE_BUFFERED_QUERY non è costretto al vero. È impostato come predefinito.

+3

Oppure potresti semplicemente fare 'DOP :: MYSQL_ATTR_INIT_COMMAND => "SET SET CARATTERE utf8, NAMES utf8" '. Virgola come separatore e senza ripetere SET. [Set-statement.html] (http://dev.mysql.com/doc/refman/5.7/en/set-statement.html) –

Problemi correlati