2011-01-15 12 views
104

Entrambi fanno la stessa cosa, solo in modo diverso?query PDO vs esecuzione

C'è qualche differenza oltre ad utilizzare prepare tra

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

e

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

?

risposta

116

query esegue un'istruzione SQL standard e richiede di eseguire correttamente l'escape di tutti i dati per evitare le Iniezioni SQL e altri problemi.

execute esegue un'istruzione preparata che consente di associare i parametri per evitare la necessità di uscire o di citare i parametri. execute funzionerà anche meglio se si sta ripetendo una query più volte. Esempio di istruzioni 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 

Le migliori pratiche è quello di attaccare con dichiarazioni preparate e execute per una maggiore sicurezza.

Consulta anche: Are PDO prepared statements sufficient to prevent SQL injection?

+0

Il collegamento conduce alla domanda con una risposta abbastanza stupida, già criticata nei commenti. –

+0

Quindi, se si utilizza una preparazione in ': calorie 'è quel tipo di equivalente di' mysql_real_escape_string() 'per interrompere le iniezioni o è necessario più di un semplice' $ sth-> bindParam (': calorie ', $ calorie) ; per aumentare la sicurezza? – Dan

+0

Perché 'query' restituisce un * PDOStatement *, invece di un * bool * come' execute'? – Leo

40

No, non sono la stessa cosa. A parte l'escaping sul lato client che fornisce, una dichiarazione preparata viene compilata sul lato server una volta, e quindi possono essere passati diversi parametri ad ogni esecuzione. Il che significa che si può 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); 

In genere vi darà un miglioramento delle prestazioni, anche se non evidente su piccola scala. Read more on prepared statements (MySQL version).

+0

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

+0

Favoloso con MySQL, ma [non funziona con tutti i driver PDO] (http://stackoverflow.com/q/39088156/4233593). –

2

Gilean's answer è ottimo, ma volevo solo aggiungere che a volte ci sono rare eccezioni alle migliori pratiche e si potrebbe voler testare il proprio ambiente in entrambi i modi per vedere cosa funzionerà meglio.

In un caso, ho scoperto che query lavorato più veloce per i miei scopi, perché ero di massa il trasferimento dei dati di fiducia da una casella di Ubuntu Linux in esecuzione PHP7 con il mal supportato Microsoft ODBC driver for MS SQL Server.

Sono arrivato a questa domanda perché avevo uno script in esecuzione lungo per uno ETL che stavo cercando di spremere per la velocità. Mi è sembrato intuitivo che query potesse essere più veloce di prepare & execute perché chiamava solo una funzione anziché due. L'operazione di associazione dei parametri fornisce una protezione eccellente, ma potrebbe essere costosa ed eventualmente evitata se non necessaria.

determinate condizioni un paio rari:

  1. Se non è possibile riutilizzare una dichiarazione preparata perché it's not supported by the Microsoft ODBC driver.

  2. Se non si è preoccupati di disinfettare l'input e la semplice escaping è accettabile. Questo potrebbe essere il caso binding certain datatypes isn't supported by the Microsoft ODBC driver.

  3. PDO::lastInsertId non è supportato dal driver Microsoft ODBC.

Ecco un metodo che ho usato per testare il mio ambiente, e, auspicabilmente, si può replicare o qualcosa di meglio nella vostra:

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 temporizzato di base per le misurazioni 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 più sperimentazione e conta diversi nel mio ambiente specifico, e costantemente ottenere tra il 20-30% risultati più veloci con query di prepare/execute

5,8128969669342 preparare
5,8688418865204 preparano
4.2948560714722 query
4.9533629417419 query
5.9051351547241 preparare
4,2060318 interrogazione
5,9672858715057 preparare
5,0667371749878 interrogazione
3,8260300159454 interrogazione
4,0791549682617 interrogazione
4,3775160312653 interrogazione
3,6910600662231 interrogazione
5,2708210945129 preparare
6,2671611309052 preparare
7,3791449069977 preparare
(7) preparare media: 6,0673267160143
(8) interrogare A verage: 4.3276024162769

Sono curioso di vedere come questo test viene confrontato in altri ambienti, come MySQL.

+0

Il problema con "prove empiriche" (o piuttosto prove artificiali) che riflettono le vostre condizioni (sconosciute) particolari e possono differire per qualcun altro, per non parlare delle prove empiriche del mondo reale. Eppure alcune persone lo danno per scontato e diffondono ulteriormente la parola. –

+0

@YourCommonSense Sono completamente d'accordo, e grazie per il vostro feedback perché ho pensato che fosse chiaro dalle mie audaci condizioni rare. Presumo una sana dose di scetticismo, ma dimentico che non è ovvio. Per favore vedi la mia risposta alla mia risposta e fammi sapere come può essere migliorata. –