2011-01-15 18 views
104

¿Ambos hacen lo mismo, solo de manera diferente?consulta de PDO vs ejecutar

¿Hay alguna diferencia entre prepare además de usar

$sth = $db->query("SELECT * FROM table"); 
$result = $sth->fetchAll(); 

y

$sth = $db->prepare("SELECT * FROM table"); 
$sth->execute(); 
$result = $sth->fetchAll(); 

?

Respuesta

116

query ejecuta una instrucción SQL estándar y requiere que escapes todos los datos para evitar las inyecciones de SQL y otros problemas.

execute ejecuta una declaración preparada que le permite enlazar parámetros para evitar la necesidad de escapar o citar los parámetros. execute también tendrá un mejor rendimiento si repite una consulta varias veces. Ejemplo de declaraciones preparadas:

$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit 
    WHERE calories < :calories AND colour = :colour'); 
$sth->bindParam(':calories', $calories); 
$sth->bindParam(':colour', $colour); 
$sth->execute(); 
// $calories or $color do not need to be escaped or quoted since the 
// data is separated from the query 

La mejor práctica es seguir con declaraciones preparadas y execute para aumentar la seguridad.

Consulte también: Are PDO prepared statements sufficient to prevent SQL injection?

+0

El enlace lleva a la pregunta con una respuesta bastante estúpida, ya criticada en los comentarios. –

+0

Así que si usas una preparación en ': calories' es ese tipo de equivalente de' mysql_real_escape_string() 'para detener las inyecciones o necesitas más que' $ sth-> bindParam (': calories', $ calories) ; 'para aumentar la seguridad? – Dan

+0

¿Por qué 'query' devuelve un * PDOStatement *, en lugar de un * bool * como' execute'? – Leo

40

No, ellos no son lo mismo. Además del escape en el lado del cliente que proporciona, una declaración preparada se compila en el servidor una vez, y luego se puede pasar diferentes parámetros en cada ejecución. Lo que significa que puede hacer:

$sth = $db->prepare("SELECT * FROM table WHERE foo = ?"); 
$sth->execute(array(1)); 
$results = $sth->fetchAll(PDO::FETCH_ASSOC); 

$sth->execute(array(2)); 
$results = $sth->fetchAll(PDO::FETCH_ASSOC); 

Por lo general, le dará una mejora en el rendimiento, aunque no se nota en una escala pequeña. Read more on prepared statements (MySQL version).

+0

Me gusta cómo explicaste por qué sería más rápido. – timfreilly

+0

Fabuloso con MySQL, pero [no funciona con todos los controladores PDO] (http://stackoverflow.com/q/39088156/4233593). –

2

Gilean's answer es genial, pero solo quería agregar que a veces existen raras excepciones a las mejores prácticas, y es posible que desee probar su entorno en ambos sentidos para ver qué funciona mejor.

En un caso, encontré que query funcionó más rápido para mis propósitos porque estaba transfiriendo a gran escala los datos de confianza de una caja de Ubuntu Linux que ejecuta PHP7 con el Microsoft ODBC driver for MS SQL Server con soporte deficiente.

Llegué a esta pregunta porque tenía un script de ejecución larga para un ETL que estaba tratando de exprimir para obtener velocidad. Me pareció intuitivo que query podría ser más rápido que prepare & execute porque llamaba solo una función en lugar de dos. La operación de enlace de parámetros proporciona una excelente protección, pero puede ser costoso y posiblemente evitado si no es necesario.

Dadas las condiciones de un par raras:

  1. Si no se puede reutilizar una declaración preparada porque it's not supported by the Microsoft ODBC driver.

  2. Si no está preocupado por la desinfección de la entrada y el simple escape es aceptable. Este puede ser el caso porque binding certain datatypes isn't supported by the Microsoft ODBC driver.

  3. PDO::lastInsertId no es compatible con el controlador ODBC de Microsoft.

Aquí hay un método que utiliza para probar mi entorno, y es de esperar que se puede replicar o algo mejor en la suya:

Para empezar, he creado una tabla básica en Microsoft SQL Server

CREATE TABLE performancetest (
    sid INT IDENTITY PRIMARY KEY, 
    id INT, 
    val VARCHAR(100) 
); 

Y ahora es una prueba básica de tiempo para las métricas de rendimiento.

$logs = []; 

$test = function (String $type, Int $count = 3000) use ($pdo, &$logs) { 
    $start = microtime(true); 
    $i = 0; 
    while ($i < $count) { 
     $sql = "INSERT INTO performancetest (id, val) OUTPUT INSERTED.sid VALUES ($i,'value $i')"; 
     if ($type === 'query') { 
      $smt = $pdo->query($sql); 
     } else { 
      $smt = $pdo->prepare($sql); 
      $smt ->execute(); 
     } 
     $sid = $smt->fetch(PDO::FETCH_ASSOC)['sid']; 
     $i++; 
    } 
    $total = (microtime(true) - $start); 
    $logs[$type] []= $total; 
    echo "$total $type\n"; 
}; 

$trials = 15; 
$i = 0; 
while ($i < $trials) { 
    if (random_int(0,1) === 0) { 
     $test('query'); 
    } else { 
     $test('prepare'); 
    } 
    $i++; 
} 

foreach ($logs as $type => $log) { 
    $total = 0; 
    foreach ($log as $record) { 
     $total += $record; 
    } 
    $count = count($log); 
    echo "($count) $type Average: ".$total/$count.PHP_EOL; 
} 

He jugado con diferentes ensayo múltiple y recuentos en mi entorno específico, y recibe constantemente entre un 20-30% más rápido con resultados query que prepare/execute

5,8128969669342 preparar
5.8688418865204 preparan
4.2948560714722 consulta
4.9533629417419 consulta
5.9051351547241 prepare
4,2060318 consulta
5,9672858715057 preparar
5,0667371749878 consulta
3,8260300159454 consulta
4,0791549682617 consulta
4,3775160312653 consulta
3,6910600662231 consulta
5,2708210945129 preparar
6,2671611309052 preparar
7,3791449069977 preparar
(7) se preparan media: 6.0673267160143
(8) consulta A verage: 4.3276024162769

Tengo curiosidad por ver cómo esta prueba se compara en otros entornos, como MySQL.

+0

El problema con las "evidencias empíricas" (o pruebas más bien artificiales) que reflejan sus condiciones particulares (desconocidas) y pueden diferir para cualquier otra persona, sin mencionar la evidencia empírica del mundo real. Sin embargo, algunas personas lo darán por sentado y difundirán más la palabra. –

+0

@YourCommonSense Estoy totalmente de acuerdo, y gracias por sus comentarios porque pensé que era claro desde mis raras y audaces condiciones. Supongo que es una buena dosis de escepticismo, pero olvide que no es obvio. Por favor, mira mi respuesta revisada y déjame saber cómo se puede mejorar. –