Consulta de PDO vs ejecutar

129

¿Ambos hacen lo mismo, pero de manera diferente?

¿Hay alguna diferencia además de usar prepareentre

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

y

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

?

Qiao
fuente

Respuestas:

145

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

executeejecuta una declaración preparada que le permite vincular parámetros para evitar la necesidad de escapar o citar los parámetros. executetambién funcionará mejor 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 las declaraciones preparadas y executeaumentar la seguridad .

Ver también: ¿Son suficientes las declaraciones preparadas para PDO para evitar la inyección de SQL?

Gilean
fuente
El enlace lleva a la pregunta con una respuesta bastante estúpida, ya criticada en los comentarios.
Su sentido común el
Entonces, si usa una preparación, ¿ : calories es ese el equivalente de mysql_real_escape_string()detener las inyecciones o necesita algo más que $sth->bindParam(':calories', $calories);aumentar la seguridad?
Dan
¿Por qué querydevuelve un PDOStatement , en lugar de un bool como execute?
Leo
1
Repetir una consulta varias veces no funciona con todos los controladores PDO .
Jeff Puckett
47

No, no son lo mismo. Además del escape en el lado del cliente que proporciona, una declaración preparada se compila en el lado del servidor una vez, y luego se pueden pasar diferentes parámetros en cada ejecución. Lo que significa que puedes 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);

En general, le brindarán una mejora en el rendimiento, aunque no se notará a pequeña escala. Lea más sobre declaraciones preparadas (versión MySQL) .

netcoder
fuente
Me gusta cómo explicaste por qué sería más rápido.
timfreilly
Fabuloso con MySQL, pero no funciona con todos los controladores PDO .
Jeff Puckett
3

La respuesta de Gilean es excelente, pero solo quería agregar que a veces hay raras excepciones a las mejores prácticas, y es posible que desee probar su entorno en ambos sentidos para ver qué funcionará mejor.

En un caso, descubrí que queryfuncionaba más rápido para mis propósitos porque estaba transfiriendo en masa datos confiables desde una caja de Ubuntu Linux que ejecuta PHP7 con el controlador ODBC de Microsoft mal soportado para MS SQL Server .

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

Dadas un par de condiciones raras :

  1. Si no puede reutilizar una declaración preparada porque no es compatible con el controlador ODBC de Microsoft .

  2. Si no está preocupado por desinfectar la entrada y el escape simple es aceptable. Este puede ser el caso porque la vinculación de ciertos tipos de datos no es compatible con el controlador ODBC de Microsoft .

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

Aquí hay un método que usé para probar mi entorno, y espero que puedas replicarlo o algo mejor en el tuyo:

Para comenzar, 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 una prueba cronometrada básica 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 queryde prepare/execute

5,8128969669342 preparar
5,8688418865204 preparar
4,2948560714722 consulta
4,9533629417419 consulta
5,9051351547241 preparar
4,332102060318 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) preparar media: 6.0673267160143
(8) consulta media: 4,3276024162769

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

Jeff Puckett
fuente
El problema con las "evidencias empíricas" (o más bien pruebas artificiales) que reflejan sus condiciones particulares (desconocidas) y pueden diferir para cualquier otra persona, y mucho menos la evidencia empírica del mundo real. Sin embargo, algunas personas lo darán por sentado y correrán la voz.
Su sentido común
@YourCommonSense Estoy completamente de acuerdo, y gracias por sus comentarios porque pensé que eso estaba claro por mis condiciones raras y audaces. Asumo una buena dosis de escepticismo, pero olvide que no es obvio. Vea mi respuesta revisada y avíseme cómo se puede mejorar.
Jeff Puckett