回答:
query
標準SQLステートメントを実行し、SQLインジェクションやその他の問題を回避するためにすべてのデータを適切にエスケープする必要があります。
execute
パラメータをバインドして、パラメータをエスケープまたは引用符で囲む必要がないようにする準備済みステートメントを実行します。execute
クエリを複数回繰り返す場合も、パフォーマンスが向上します。準備されたステートメントの例:
$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
ベストプラクティスは、準備されたステートメントに固執し、execute
セキュリティを強化することです。
: calories
するのmysql_real_escape_string()
と同じようなもの$sth->bindParam(':calories', $calories);
ですか、それともセキュリティを高めるためだけではありませんか?
query
返すのですか?execute
いいえ、同じではありません。提供されるクライアント側でのエスケープとは別に、準備されたステートメントはサーバー側で1回コンパイルされ、実行ごとに異なるパラメーターを渡すことができます。つまり、次のことができます。
$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);
小規模では目立ちませんが、通常はパフォーマンスが向上します。準備された文(MySQLバージョン)に続きを読みます。
Gileanの答えはすばらしいですが、ベストプラクティスにはまれな例外があることもあるので、両方の方法で環境をテストして、何が最適に機能するかを確認することもできます。
あるケースでは、MS SQL Serverのquery
サポートが不十分なMicrosoft ODBCドライバーを使用して、PHP7を実行しているUbuntu Linuxボックスから信頼できるデータを一括転送していたため、それが私の目的のためにより速く機能することがわかりました。
この質問にたどり着いたのは、ETLのスクリプトを長時間実行していて、速度を上げようとしていたためです。2つではなく1つの関数しか呼び出さなかったためquery
、prepare
&よりも高速である可能性があることは、私には直感的に思えましたexecute
。パラメータバインディング操作は優れた保護を提供しますが、コストがかかる可能性があり、不要な場合は回避される場合があります。
いくつかのまれな条件を考えると:
Microsoft ODBCドライバーでサポートされていないために、準備されたステートメントを再利用できない場合。
入力のサニタイズを心配していない場合は、単純なエスケープで問題ありません。これは、特定のデータ型のバインドがMicrosoft ODBCドライバーでサポートされていないために発生することがあります。
PDO::lastInsertId
Microsoft ODBCドライバーではサポートされていません。
ここに私が私の環境をテストするために使用した方法があります。うまくいけば、あなたはそれをあなたの環境で複製することができます:
まず、Microsoft SQL Serverで基本的なテーブルを作成しました
CREATE TABLE performancetest (
sid INT IDENTITY PRIMARY KEY,
id INT,
val VARCHAR(100)
);
そして今、パフォーマンスメトリックの基本的な時限テスト。
$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;
}
私は特定の環境で複数の異なるトライアルとカウントを試してきましたがquery
、prepare
/ よりも20〜30%速い結果が一貫して得られましたexecute
5.8128969669342は準備
5.8688418865204が準備
4.2948560714722クエリ
4.9533629417419クエリ
5.9051351547241が準備
4.332102060318クエリ
5.9672858715057が準備
5.0667371749878クエリ
3.8260300159454クエリ
4.0791549682617クエリ
4.3775160312653クエリ
3.6910600662231クエリ
5.2708210945129が準備
6.2671611309052が準備
7.3791449069977が準備
6.0673267160143:平均準備(7)
(8)クエリ平均:4.3276024162769を
MySQLのような他の環境でこのテストがどのように比較されるか知りたいです。