web-dev-qa-db-ja.com

PDOのクエリと実行

彼らは両方とも同じことをしますか、異なるだけですか?

prepareを使用する以外に違いはありますか

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

そして

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

127
Qiao

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インジェクションを防ぐのに十分ですか?

139
Gilean

いいえ、同じではありません。それが提供するクライアント側でのエスケープとは別に、準備されたステートメントはサーバー側で一度コンパイルされ、異なるパラメータを渡すことができます実行ごとに。それはあなたができることを意味します:

$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バージョン)

45
netcoder

Gileanの答え は素晴らしいですが、ベストプラクティスにはまれな例外がある場合があることを付け加えたかったので、両方の方法で環境をテストして最適な結果を確認することをお勧めします。

あるケースでは、queryは、目的に応じて高速に動作することがわかりました。これは、サポートが不十分なPHP7を実行するUbuntu Linuxボックスから信頼できるデータを一括転送するためです Microsoft ODBCドライバーSQL Server

ETL のスクリプトを長時間実行していたため、この質問にたどり着きました。 queryは2つではなく1つの関数のみを呼び出していたため、prepareおよび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/executeよりも一貫して20〜30%高速な結果になります

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準備
(7)準備平均:6.0673267160143
(8)クエリ平均:4.3276024162769

MySQLのような他の環境でこのテストがどのように比較されるのか興味があります。

3
Jeff Puckett