Consulta do PDO vs Execução

129

Ambos fazem a mesma coisa, apenas de maneira diferente?

Existe alguma diferença além de usar prepareentre

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

e

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

?

Qiao
fonte

Respostas:

145

query executa uma instrução SQL padrão e requer que você escape adequadamente de todos os dados para evitar injeções de SQL e outros problemas.

executeexecuta uma instrução preparada que permite vincular parâmetros para evitar a necessidade de escapar ou citar os parâmetros. executetambém terá um desempenho melhor se você estiver repetindo uma consulta várias vezes. Exemplo de declarações preparadas:

$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

A melhor prática é seguir instruções preparadas e executeaumentar a segurança .

Consulte também: As instruções preparadas para DOP são suficientes para impedir a injeção de SQL?

Gilean
fonte
O link leva à pergunta com uma resposta bastante estúpida, já criticada nos comentários.
Seu senso comum
Então, se você usa um preparado, esse : calories é o equivalente mysql_real_escape_string()a interromper as injeções ou precisa mais do que apenas $sth->bindParam(':calories', $calories);para aumentar a segurança?
Dan
Por que queryretornar uma declaração PDOS , em vez de um bool como execute?
Leo
1
Repetir uma consulta várias vezes não funciona com todos os drivers PDO .
Jeff Puckett
47

Não, eles não são os mesmos. Além do escape no lado do cliente que ele fornece, uma instrução preparada é compilada no lado do servidor uma vez e, em seguida, podem ser transmitidos parâmetros diferentes a cada execução. O que significa que você pode fazer:

$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);

Eles geralmente proporcionam uma melhora no desempenho, embora não sejam perceptíveis em pequena escala. Leia mais sobre instruções preparadas (versão MySQL) .

codificador de rede
fonte
Gosto da maneira como você explicou por que seria mais rápido.
timfreilly
Fabuloso com o MySQL, mas não funciona com todos os drivers DOP .
Jeff Puckett
3

A resposta de Gilean é ótima, mas eu só queria acrescentar que, às vezes, existem raras exceções às melhores práticas, e você pode testar seu ambiente nos dois sentidos para ver o que funcionará melhor.

Em um caso, descobri que queryfuncionava mais rápido para meus objetivos porque estava transferindo dados confiáveis ​​em massa de uma caixa Ubuntu Linux executando PHP7 com o driver Microsoft ODBC da Microsoft com suporte insuficiente para o MS SQL Server .

Cheguei a essa pergunta porque tinha um script de longa duração para um ETL que estava tentando pressionar pela velocidade. Pareceu-me intuitivo que querypoderia ser mais rápido do que prepare& executeporque estava chamando apenas uma função em vez de duas. A operação de ligação de parâmetro fornece excelente proteção, mas pode ser cara e possivelmente evitada se desnecessária.

Dadas algumas condições raras :

  1. Se você não conseguir reutilizar uma instrução preparada porque ela não é suportada pelo driver ODBC da Microsoft .

  2. Se você não estiver preocupado com a higienização das entradas e com a simples fuga, é aceitável. Pode ser esse o caso, porque a vinculação de certos tipos de dados não é suportada pelo driver ODBC da Microsoft .

  3. PDO::lastInsertId não é suportado pelo driver ODBC da Microsoft.

Aqui está um método que eu usei para testar meu ambiente e espero que você possa replicá-lo ou algo melhor no seu:

Para começar, eu criei uma tabela básica no Microsoft SQL Server

CREATE TABLE performancetest (
    sid INT IDENTITY PRIMARY KEY,
    id INT,
    val VARCHAR(100)
);

E agora um teste cronometrado básico para métricas de desempenho.

$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;
}

Eu joguei com o julgamento diferente múltipla e conta no meu ambiente específico, e consistentemente ficar entre 20-30% mais rápido resultados com queryque prepare/execute

5,8128969669342 preparar
5,8688418865204 preparar
4,2948560714722 consulta
4,9533629417419 consulta
5,9051351547241 preparar
4,332102060318 consulta
5,9672858715057 preparar
5,0667371749878 consulta
3,8260300159454 consulta
4,0791549682617 consulta
4,3775160312653 consulta
3,6910600662231 consulta
5,2708210945129 preparar
6,2671611309052 preparar
7,3791449069977 preparar
(7) preparar média: 6,0673267160143
(8) consulta média: 4,3276024162769

Estou curioso para ver como esse teste se compara em outros ambientes, como o MySQL.

Jeff Puckett
fonte
O problema das "evidências empíricas" (ou testes artificiais) de que elas refletem suas condições particulares (desconhecidas) e podem diferir para qualquer outra pessoa, sem falar nas evidências empíricas do mundo real. No entanto, algumas pessoas tomam isso como garantido e espalham a palavra ainda mais.
Seu senso comum
@YourCommonSense Concordo plenamente, e obrigado por seus comentários, porque achei que isso estava claro em minhas raras condições ousadas. Presumo uma dose saudável de ceticismo, mas esqueço que não é óbvio. Por favor, veja minha resposta revisada e deixe-me saber como ela pode ser melhorada.
Jeff Puckett