PDO::fetchAll vs PDO::fetch en un bucle


Solo una pregunta rápida.

¿Hay alguna diferencia de rendimiento entre usar PDO::fetchAll() y PDO::fetch() en un bucle (para conjuntos de resultados grandes)?

Estoy buscando objetos de una clase definida por el usuario, si eso hace alguna diferencia.

Mi suposición inicial sin educación fue que fetchAll podría ser más rápido porque PDO puede realizar varias operaciones en una instrucción, mientras que mysql_query solo puede ejecutar una. Sin embargo, tengo poco conocimiento del funcionamiento interno de la DOP y el la documentación no dice nada sobre esto, y si fetchAll() es simplemente un bucle del lado de PHP volcado en un array.

Alguna ayuda?

Author: Lotus Notes, 2010-05-05

7 answers

Poco benchmark con 200k registros aleatorios. Como era de esperar, el método fetchAll es más rápido pero requiere más memoria.

Result :
fetchAll : 0.35965991020203s, 100249408b
fetch : 0.39197015762329s, 440b

El código de referencia utilizado:

<?php
// First benchmark : speed
$dbh = new PDO('mysql:dbname=testage;dbhost=localhost', 'root', '');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = 'SELECT * FROM test_table WHERE 1';
$stmt = $dbh->query($sql);
$data = array();
$start_all = microtime(true);
$data = $stmt->fetchAll();
$end_all = microtime(true);

$stmt = $dbh->query($sql);
$data = array();
$start_one = microtime(true);
while($data = $stmt->fetch()){}
$end_one = microtime(true);

// Second benchmark : memory usage
$stmt = $dbh->query($sql);
$data = array();
$memory_start_all = memory_get_usage();
$data = $stmt->fetchAll();
$memory_end_all = memory_get_usage();

$stmt = $dbh->query($sql);
$data = array();
$memory_end_one = 0;
$memory_start_one = memory_get_usage();
while($data = $stmt->fetch()){
  $memory_end_one = max($memory_end_one, memory_get_usage());
}

echo 'Result : <br/>
fetchAll : ' . ($end_all - $start_all) . 's, ' . ($memory_end_all - $memory_start_all) . 'b<br/>
fetch : ' . ($end_one - $start_one) . 's, ' . ($memory_end_one - $memory_start_one) . 'b<br/>';
 71
Author: Arkh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-05 08:25:52

Una cosa sobre PHP que he encontrado que es cierta casi siempre es que una función que implemente usted mismo casi siempre será más lenta que el equivalente de PHP. Esto se debe a que cuando algo se implementa en PHP no tiene todas las optimizaciones de tiempo de compilación que tiene C (en las que PHP está escrito) y hay una alta sobrecarga de llamadas a funciones PHP.

 10
Author: Kendall Hopkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-05 04:39:38

@ Arkh

// $data in this case is an array of rows;

$data = $stmt->fetchAll();


// $data in this case is just one row after each loop;

while($data = $stmt->fetch()){}


// Try using

$i = 0;

while($data[$i++] = $stmt->fetch()){}

La diferencia de memoria debería volverse neglijable

 9
Author: Mihai Stancu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-03 16:12:24

Todos los puntos de referencia por encima de los cuales miden "huella de memoria" son en realidad incorrectos por la razón muy simple.

PDO por defecto carga todas las cosas en la memoria y no le importa si usa fetch o fetchAll. Para obtener realmente los beneficios de la consulta sin búfer, debe indicar a PDO que use consultas sin búfer:

$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

En ese caso, verá una gran diferencia en la huella de memoria del script

 8
Author: iVariable,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-02 08:41:20

Como decía Mihai Stancu, casi no hay diferencia de memoria, aunque fetchAll vence a fetch + while.

Result : 
fetchAll : 0.160676956177s, 118539304b
fetch : 0.121752023697s, 118544392b

Obtuve los resultados anteriores al ejecutar correctamente:

$i = 0;
while($data[$i++] = $stmt->fetch()){
    //
}

Así que el fetchAll consume menos memoria, pero fetch + mientras que es más rápido! :)

 4
Author: Rihards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-24 23:49:18

Pero seguramente si usted está almacenando los datos obtenidos en una matriz, el uso de memoria será igual?

<?php
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
// database to use
define('DB', 'test');
try
{
   $dbh = new \PDO('mysql:dbname='. DB .';host='. DB_HOST, DB_USER, DB_PASS);   $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   $sql = 'SELECT * FROM users WHERE 1';
   $stmt = $dbh->query($sql);
   $data = array();
   $start_all = microtime(true);
   $data = $stmt->fetchAll();
   $end_all = microtime(true);

   $stmt = $dbh->query($sql);
   $data = array();
   $start_one = microtime(true);
   while($data = $stmt->fetch()){}
   $end_one = microtime(true);

   // Second benchmark : memory usage
   $stmt = $dbh->query($sql);
   $data = array();
   $memory_start_all = memory_get_usage();
   $data = $stmt->fetchAll();
   $memory_end_all = memory_get_usage();

   $stmt = $dbh->query($sql);
   $data = array();
   $memory_end_one = 0;
   $memory_start_one = memory_get_usage();
   while($data[] = $stmt->fetch()){
     $memory_end_one = max($memory_end_one, memory_get_usage());
   }

   echo 'Result : <br/>
   fetchAll : ' . ($end_all - $start_all) . 's, ' . ($memory_end_all - $memory_start_all) . 'b<br/>
   fetch : ' . ($end_one - $start_one) . 's, ' . ($memory_end_one - $memory_start_one) . 'b<br/>';
}
catch ( PDOException $e )
{
   echo $e->getMessage();
}
?>

Result : 
fetchAll : 2.6941299438477E-5s, 9824b
fetch : 1.5974044799805E-5s, 9824b
 3
Author: Andy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-28 13:04:10

Sé que este es un tema antiguo, pero me encuentro con esto teniendo la misma pregunta. Habiendo ejecutado mi propio "benchmark" simple y leyendo lo que otros escribieron aquí, llegué a la conclusión de que esto no es una ciencia exacta y, si bien uno debe esforzarse por escribir el código de calidad y luz, no tiene sentido perder demasiado tiempo al inicio del proyecto.

Mi sugerencia es: Recopilar datos ejecutando el código (¿en beta?) por un tiempo y luego empezar a optimizar.

En mi simple benchmark (solo tiempo de ejecución probado) Tengo resultados que varían entre el 5% y el 50% en AMBOS sentidos. Corro ambas opciones en el mismo script, pero cuando corro fetch + mientras que primero ha sido más rápido que fetchall y viceversa. (Sé que debería haberlos ejecutado solo y un par de cientos de veces obtener la mediana y la media y luego comparar, pero - como he dicho al principio - llegué a la conclusión de que en mi caso es demasiado pronto para empezar a hacerlo.)

 1
Author: StrayObject,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-02 23:43:00