Respostas:
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.
execute
executa uma instrução preparada que permite vincular parâmetros para evitar a necessidade de escapar ou citar os parâmetros. execute
també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 execute
aumentar a segurança .
Consulte também: As instruções preparadas para DOP são suficientes para impedir a injeção de SQL?
: 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?
query
retornar uma declaração PDOS , em vez de um bool como execute
?
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) .
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 query
funcionava 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 query
poderia ser mais rápido do que prepare
& execute
porque 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 :
Se você não conseguir reutilizar uma instrução preparada porque ela não é suportada pelo driver ODBC da Microsoft .
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 .
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 query
que 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.