PDO-Abfrage vs ausführen

129

Tun beide dasselbe, nur unterschiedlich?

Gibt es einen Unterschied neben der Verwendung preparezwischen

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

und

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

?

Qiao
quelle

Antworten:

145

query führt eine Standard-SQL-Anweisung aus und erfordert, dass Sie alle Daten ordnungsgemäß maskieren, um SQL-Injections und andere Probleme zu vermeiden.

executefü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. executeDies 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 executedie Sicherheit zu erhöhen .

Siehe auch: Reichen PDO-vorbereitete Anweisungen aus, um eine SQL-Injection zu verhindern?

Gilean
quelle
Der Link führt zu der Frage mit ziemlich dummer Antwort, die bereits in Kommentaren kritisiert wurde.
Ihr
Wenn Sie also ein Präparat verwenden : calories , entspricht dies mysql_real_escape_string()dem Absetzen von Injektionen, oder benötigen Sie mehr als nur $sth->bindParam(':calories', $calories);die Sicherheit?
Dan
Warum wird queryein PDOStatement anstelle eines Bool- ähnlichen zurückgegeben execute?
Leo
1
Das mehrmalige Wiederholen einer Abfrage funktioniert nicht mit allen PDO-Treibern .
Jeff Puckett
47

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) .

Netcoder
quelle
Mir gefällt, wie Sie erklärt haben, warum es schneller gehen würde.
Timfreilly
Fabelhaft mit MySQL, funktioniert aber nicht mit allen PDO-Treibern .
Jeff Puckett
3

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 queryfü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 queryes schneller sein könnte als prepare&, executeweil 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 :

  1. Wenn Sie eine vorbereitete Anweisung nicht wiederverwenden können, weil sie vom Microsoft ODBC-Treiber nicht unterstützt wird .

  2. 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 .

  3. 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 queryals 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.

Jeff Puckett
quelle
Das Problem mit "empirischen Beweisen" (oder eher künstlichen Tests), dass sie Ihre (unbekannten) besonderen Bedingungen widerspiegeln und für jeden anderen unterschiedlich sein können, geschweige denn für die empirischen Beweise der realen Welt. Einige Leute werden es jedoch für selbstverständlich halten und das Wort weiter verbreiten.
Ihr gesunder Menschenverstand
@YourCommonSense Ich stimme vollkommen zu und danke für Ihr Feedback, da ich dachte, dass dies aus meinen kühnen, seltenen Bedingungen hervorgeht. Ich gehe von einer gesunden Dosis Skepsis aus, vergesse aber, dass dies nicht offensichtlich ist. Bitte lesen Sie meine überarbeitete Antwort und lassen Sie mich wissen, wie sie verbessert werden kann.
Jeff Puckett