Risposte:
query
esegue un'istruzione SQL standard e richiede di evitare correttamente tutti i dati per evitare iniezioni SQL e altri problemi.
execute
esegue un'istruzione preparata che consente di associare i parametri per evitare la necessità di sfuggire o citare i parametri. execute
funzionerà anche meglio se stai ripetendo una query più volte. Esempio di dichiarazioni preparate:
$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 migliore pratica è attenersi a dichiarazioni preparate e execute
per una maggiore sicurezza .
Vedi anche: Le istruzioni preparate per DOP sono sufficienti per prevenire l'iniezione di SQL?
: calories
quel tipo di equivalente di mysql_real_escape_string()
per fermare le iniezioni o hai bisogno di qualcosa di più che $sth->bindParam(':calories', $calories);
aumentare la sicurezza?
query
restituisce un PDOStatement , invece di un bool come execute
?
No, non sono gli stessi. A parte l'escaping sul lato client che fornisce, un'istruzione preparata viene compilata sul lato server una volta, quindi può essere passato parametri diversi ad ogni esecuzione. Ciò significa che puoi fare:
$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);
Generalmente ti daranno un miglioramento delle prestazioni, anche se non evidente su piccola scala. Maggiori informazioni sulle dichiarazioni preparate (versione MySQL) .
La risposta di Gilean è ottima, ma volevo solo aggiungere che a volte ci sono rare eccezioni alle migliori pratiche e potresti voler testare il tuo ambiente in entrambi i modi per vedere cosa funzionerà meglio.
In un caso, ho scoperto che ha query
funzionato più velocemente per i miei scopi perché stavo trasferendo in blocco dati attendibili da una scatola Ubuntu Linux che esegue PHP7 con il driver Microsoft ODBC scarsamente supportato per MS SQL Server .
Sono arrivato a questa domanda perché avevo una sceneggiatura di lunga durata per un ETL che stavo cercando di spremere per la velocità. Mi è sembrato intuitivo che query
potrebbe essere più veloce di prepare
e execute
perché chiamava solo una funzione anziché due. L'operazione di associazione dei parametri offre un'eccellente protezione, ma potrebbe essere costosa ed eventualmente evitata se non necessaria.
Date un paio di condizioni rare :
Se non è possibile riutilizzare un'istruzione preparata perché non è supportata dal driver Microsoft ODBC .
Se non sei preoccupato per la sanificazione dell'input e la semplice fuga è accettabile. Questo può essere il caso perché l' associazione di determinati tipi di dati non è supportata dal driver Microsoft ODBC .
PDO::lastInsertId
non è supportato dal driver ODBC Microsoft.
Ecco un metodo che ho usato per testare il mio ambiente e spero che tu possa replicarlo o qualcosa di meglio nel tuo:
Per iniziare, ho creato una tabella di base in Microsoft SQL Server
CREATE TABLE performancetest (
sid INT IDENTITY PRIMARY KEY,
id INT,
val VARCHAR(100)
);
E ora un test cronometrato di base per le metriche delle prestazioni.
$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;
}
Ho giocato con diverse prove e conteggi nel mio ambiente specifico e ottengo costantemente risultati più rapidi del 20-30% con query
rispetto a prepare
/execute
5,8128969669342 preparare
5,8688418865204 preparare
4,2948560714722 ricerca
4,9533629417419 ricerca
5,9051351547241 preparare
4,332102060318 interrogazione
5,9672858715057 preparare
5,0667371749878 ricerca
3,8260300159454 ricerca
4,0791549682617 ricerca
4,3775160312653 ricerca
3,6910600662231 ricerca
5,2708210945129 preparare
6,2671611309052 preparare
7,3791449069977 preparare
(7) preparare media: 6,0673267160143
(8) interrogazione media: 4,3276024162769
Sono curioso di vedere come questo test si confronta in altri ambienti, come MySQL.