Query PDO vs esecuzione


129

Entrambi fanno la stessa cosa, solo in modo diverso?

C'è qualche differenza oltre a usare in preparemezzo

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

e

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

?

Risposte:


145

query esegue un'istruzione SQL standard e richiede di evitare correttamente tutti i dati per evitare iniezioni SQL e altri problemi.

executeesegue un'istruzione preparata che consente di associare i parametri per evitare la necessità di sfuggire o citare i parametri. executefunzionerà 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 executeper una maggiore sicurezza .

Vedi anche: Le istruzioni preparate per DOP sono sufficienti per prevenire l'iniezione di SQL?


Il link porta alla domanda con una risposta abbastanza stupida, già criticata nei commenti.
Il tuo buon senso

Quindi, se usi un preparato per : 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?
Dan,

Perché queryrestituisce un PDOStatement , invece di un bool come execute?
Leone,

1
La ripetizione di una query più volte non funziona con tutti i driver PDO .
Jeff Puckett,

47

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


Mi piace il modo in cui hai spiegato perché sarebbe più veloce.
timfreilly,


3

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 queryfunzionato 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 querypotrebbe essere più veloce di preparee executeperché 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 :

  1. Se non è possibile riutilizzare un'istruzione preparata perché non è supportata dal driver Microsoft ODBC .

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

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


Il problema con "prove empiriche" (o piuttosto prove artificiali) che riflettono le tue condizioni (sconosciute) particolari e che possono differire per chiunque altro, per non parlare delle prove empiriche del mondo reale. Eppure alcune persone lo daranno per scontato e spargeranno ulteriormente la voce.
Il tuo buon senso

@YourCommonSense Sono completamente d'accordo, e grazie per il tuo feedback perché pensavo fosse chiaro dalle mie audaci rare condizioni. Presumo una buona dose di scetticismo, ma dimentico che non è ovvio. Si prega di vedere la mia risposta modificata e fammi sapere come può essere migliorato.
Jeff Puckett,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.