PDOのクエリと実行


129

彼らは両方とも同じことをしますか?

使用した以外の任意の差があるprepare間は、

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

そして

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

回答:


145

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セキュリティを強化することです。

参照:PDO準備済みステートメントはSQLインジェクションを防ぐのに十分ですか?


リンクは、コメントですでに批判されている、かなり愚かな答えのある質問につながります。
常識

では、準備を使用する場合、注射を停止: calories するのmysql_real_escape_string()と同じようなもの$sth->bindParam(':calories', $calories);ですか、それともセキュリティを高めるためだけではありませんか?
Dan

のようなboolの代わりに、なぜPDOStatementをquery返すのですか?execute
レオ


47

いいえ、同じではありません。提供されるクライアント側でのエスケープとは別に、準備されたステートメントはサーバー側で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バージョン)に続きを読みます


私はあなたがなぜそれがより速くなるかを説明した方法が好きです。
timfreilly


3

Gileanの答えはすばらしいですが、ベストプラクティスにはまれな例外があることもあるので、両方の方法で環境をテストして、何が最適に機能するかを確認することもできます。

あるケースでは、MS SQL Serverのqueryサポートが不十分なMicrosoft ODBCドライバーを使用して、PHP7を実行しているUbuntu Linuxボックスから信頼できるデータを一括転送していたため、それが私の目的のためにより速く機能することがわかりました。

この質問にたどり着いたのは、ETLのスクリプトを長時間実行していて、速度を上げようとしていたためです。2つではなく1つの関数しか呼び出さなかったためqueryprepare&よりも高速である可能性があることは、私には直感的に思えましたexecute。パラメータバインディング操作は優れた保護を提供しますが、コストがかかる可能性があり、不要な場合は回避される場合があります。

いくつかのまれな条件を考えると

  1. Microsoft ODBCドライバーでサポートされていないため、準備されたステートメントを再利用できない場合。

  2. 入力のサニタイズを心配していない場合は、単純なエスケープで問題ありません。これは、特定のデータ型のバインドがMicrosoft ODBCドライバーでサポートされていないために発生することがあります

  3. 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;
}

私は特定の環境で複数の異なるトライアルとカウントを試してきましたがqueryprepare/ よりも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のような他の環境でこのテストがどのように比較されるか知りたいです。


「経験的証拠」(またはむしろ人為的なテスト)の問題は、それらが(未知の)特定の条件を反映し、実際の経験的証拠はもちろん、他の人とは異なる可能性があることです。それでも、一部の人々はそれを当たり前のことと考え、その言葉をさらに広めるでしょう。
あなたの常識

@YourCommonSense私は完全に同意し、私の大胆な珍しい状況からそれが明らかであると思ったので、フィードバックに感謝します。私は健康的な線量の懐疑論を想定していますが、それが明白ではないことを忘れてください。私の修正された私の答えを見て、それがどのように改善できるか教えてください。
Jeff Puckett
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.