Antworten:
query
führt eine Standard-SQL-Anweisung aus und erfordert, dass Sie alle Daten ordnungsgemäß maskieren, um SQL-Injections und andere Probleme zu vermeiden.
execute
führt eine vorbereitete Anweisung aus, mit der Sie Parameter binden können, um zu vermeiden, dass die Parameter maskiert oder in Anführungszeichen gesetzt werden müssen. execute
Dies ist auch dann besser, wenn Sie eine Abfrage mehrmals wiederholen. Beispiel für vorbereitete Aussagen:
$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
Es wird empfohlen, sich an vorbereitete Aussagen zu halten und execute
die Sicherheit zu erhöhen .
Siehe auch: Reichen PDO-vorbereitete Anweisungen aus, um eine SQL-Injection zu verhindern?
: calories
, entspricht dies mysql_real_escape_string()
dem Absetzen von Injektionen, oder benötigen Sie mehr als nur $sth->bindParam(':calories', $calories);
die Sicherheit?
query
ein PDOStatement anstelle eines Bool- ähnlichen zurückgegeben execute
?
Nein, sie sind nicht gleich. Abgesehen von der Escape-Funktion auf der Clientseite, die bereitgestellt wird , wird eine vorbereitete Anweisung auf der Serverseite einmal kompiliert und kann dann bei jeder Ausführung an andere Parameter übergeben werden. Was bedeutet, dass Sie Folgendes tun können:
$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);
Sie führen im Allgemeinen zu einer Leistungsverbesserung, obwohl sie im kleinen Maßstab nicht erkennbar sind. Lesen Sie mehr über vorbereitete Anweisungen (MySQL-Version) .
Gileans Antwort ist großartig, aber ich wollte nur hinzufügen, dass es manchmal seltene Ausnahmen zu Best Practices gibt, und Sie möchten möglicherweise Ihre Umgebung in beide Richtungen testen, um herauszufinden, was am besten funktioniert.
In einem Fall stellte ich fest, dass dies query
für meine Zwecke schneller funktionierte, da ich vertrauenswürdige Daten von einer Ubuntu Linux-Box mit PHP7 mit dem schlecht unterstützten Microsoft ODBC-Treiber für MS SQL Server massenweise übertrug .
Ich bin zu dieser Frage gekommen, weil ich ein lang laufendes Skript für eine ETL hatte , das ich aus Geschwindigkeitsgründen drücken wollte. Es schien mir intuitiv zu sein, dass query
es schneller sein könnte als prepare
&, execute
weil es nur eine Funktion anstelle von zwei aufrief. Die Parameterbindungsoperation bietet einen hervorragenden Schutz, kann jedoch teuer sein und möglicherweise vermieden werden, wenn dies nicht erforderlich ist.
Angesichts einiger seltener Bedingungen :
Wenn Sie eine vorbereitete Anweisung nicht wiederverwenden können, weil sie vom Microsoft ODBC-Treiber nicht unterstützt wird .
Wenn Sie sich keine Sorgen über die Bereinigung von Eingaben machen und ein einfaches Entkommen möglich ist, ist dies akzeptabel. Dies kann der Fall sein, da das Binden bestimmter Datentypen vom Microsoft ODBC-Treiber nicht unterstützt wird .
PDO::lastInsertId
wird vom Microsoft ODBC-Treiber nicht unterstützt.
Hier ist eine Methode, mit der ich meine Umgebung getestet habe, und hoffentlich können Sie sie oder etwas Besseres in Ihrer replizieren:
Zu Beginn habe ich eine Basistabelle in Microsoft SQL Server erstellt
CREATE TABLE performancetest (
sid INT IDENTITY PRIMARY KEY,
id INT,
val VARCHAR(100)
);
Und jetzt ein grundlegender zeitgesteuerter Test für Leistungsmetriken.
$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;
}
Ich habe mit mehreren verschiedenen Testversionen und Zählungen in meiner spezifischen Umgebung gespielt und erhalte durchweg zwischen 20 und 30% schnellere Ergebnisse query
als prepare
/execute
5,8128969669342 vorbereiten
5,8688418865204 vorbereiten
4,2948560714722 Abfrage
4,9533629417419 Abfrage
5,9051351547241 vorbereiten
4,332102060318 Abfrage
5,9672858715057 vorbereiten
5,0667371749878 Abfrage
3,8260300159454 Abfrage
4,0791549682617 Abfrage
4,3775160312653 Abfrage
3,6910600662231 Abfrage
5,2708210945129 vorbereiten
6,2671611309052 vorbereiten
7,3791449069977 vorbereiten
(7) vorbereiten Durchschnitt: 6,0673267160143
(8) Abfrage Durchschnitt: 4,3276024162769
Ich bin gespannt, wie sich dieser Test in anderen Umgebungen wie MySQL verhält.