Permintaan PDO vs eksekusi

129

Apakah mereka berdua melakukan hal yang sama, hanya berbeda?

Apakah ada perbedaan selain menggunakan preparekeduanya

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

dan

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

?

Qiao
sumber

Jawaban:

145

query menjalankan pernyataan SQL standar dan mengharuskan Anda untuk melarikan diri semua data dengan benar untuk menghindari Suntikan SQL dan masalah lainnya.

executemenjalankan pernyataan yang disiapkan yang memungkinkan Anda untuk mengikat parameter untuk menghindari keharusan untuk melarikan diri atau mengutip parameter. executejuga akan berkinerja lebih baik jika Anda mengulangi permintaan beberapa kali. Contoh pernyataan yang disiapkan:

$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

Praktik terbaik adalah tetap dengan pernyataan yang disiapkan dan executeuntuk meningkatkan keamanan .

Lihat juga: Apakah pernyataan yang disiapkan PDO cukup untuk mencegah injeksi SQL?

Gilean
sumber
Tautan mengarah ke pertanyaan dengan jawaban yang cukup bodoh, sudah dikritik dalam komentar.
Akal Sehat Anda
Jadi jika Anda menggunakan persiapan pada : calories apakah itu setara dengan mysql_real_escape_string()untuk menghentikan suntikan atau apakah Anda memerlukan lebih dari sekedar $sth->bindParam(':calories', $calories);untuk meningkatkan keamanan?
Dan
Mengapa querymengembalikan PDOStatement , bukannya bool like execute?
Leo
1
Mengulang kueri beberapa kali tidak bekerja dengan semua driver PDO .
Jeff Puckett
47

Tidak, mereka tidak sama. Selain melarikan diri pada sisi klien yang disediakannya, pernyataan yang sudah disiapkan dikompilasi di sisi server sekali, dan kemudian dapat melewati parameter yang berbeda pada setiap eksekusi. Yang berarti Anda dapat melakukan:

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

Mereka umumnya akan memberi Anda peningkatan kinerja, meskipun tidak terlihat dalam skala kecil. Baca lebih lanjut tentang pernyataan yang disiapkan (versi MySQL) .

netcoder
sumber
Saya suka cara Anda menjelaskan mengapa akan lebih cepat.
timfreilly
Luar biasa dengan MySQL, tetapi tidak bekerja dengan semua driver PDO .
Jeff Puckett
3

Jawaban Gilean sangat bagus, tetapi saya hanya ingin menambahkan bahwa kadang-kadang ada pengecualian langka untuk praktik terbaik, dan Anda mungkin ingin menguji lingkungan Anda dengan dua cara untuk melihat apa yang akan bekerja paling baik.

Dalam satu kasus, saya menemukan bahwa itu querybekerja lebih cepat untuk tujuan saya karena saya secara massal mentransfer data tepercaya dari kotak Linux Ubuntu yang menjalankan PHP7 dengan driver ODBC Microsoft yang tidak didukung untuk MS SQL Server .

Saya sampai pada pertanyaan ini karena saya memiliki skrip berjalan lama untuk ETL yang saya coba peras untuk kecepatan. Tampaknya intuitif bagi saya yang querybisa lebih cepat daripada prepare& executekarena itu hanya memanggil satu fungsi, bukan dua. Operasi pengikatan parameter memberikan perlindungan yang sangat baik, tetapi mungkin mahal dan mungkin dihindari jika tidak perlu.

Diberikan beberapa kondisi langka :

  1. Jika Anda tidak dapat menggunakan kembali pernyataan yang disiapkan karena tidak didukung oleh driver Microsoft ODBC .

  2. Jika Anda tidak khawatir tentang sanitasi input dan pelolosan sederhana dapat diterima. Ini mungkin terjadi karena mengikat tipe data tertentu tidak didukung oleh driver Microsoft ODBC .

  3. PDO::lastInsertId tidak didukung oleh driver Microsoft ODBC.

Inilah metode yang saya gunakan untuk menguji lingkungan saya, dan mudah-mudahan Anda dapat meniru atau sesuatu yang lebih baik di lingkungan Anda:

Untuk memulai, saya telah membuat tabel dasar di Microsoft SQL Server

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

Dan sekarang tes dasar waktunya untuk metrik kinerja.

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

Saya telah bermain dengan beberapa percobaan dan hitungan berbeda di lingkungan spesifik saya, dan secara konsisten mendapatkan antara 20-30% hasil lebih cepat dengan querydaripada prepare/execute

5,8128969669342 mempersiapkan
5,8688418865204 mempersiapkan
4,2948560714722 permintaan
4,9533629417419 permintaan
5,9051351547241 mempersiapkan
4,332102060318 permintaan
5,9672858715057 mempersiapkan
5,0667371749878 permintaan
3,8260300159454 permintaan
4,0791549682617 permintaan
4,3775160312653 permintaan
3,6910600662231 permintaan
5,2708210945129 mempersiapkan
6,2671611309052 mempersiapkan
7,3791449069977 mempersiapkan
(7) mempersiapkan rata-rata: 6,0673267160143
(8) permintaan rata-rata: 4,3276024162769

Saya ingin tahu bagaimana perbandingan tes ini di lingkungan lain, seperti MySQL.

Jeff Puckett
sumber
Masalah dengan "bukti empiris" (atau lebih tepatnya tes buatan) yang mencerminkan kondisi khusus Anda (tidak diketahui) dan mungkin berbeda untuk orang lain, apalagi bukti empiris dunia nyata. Namun beberapa orang akan menerima begitu saja dan menyebarkan berita lebih lanjut.
Akal Sehat Anda
@YourCommonSense Saya setuju sepenuhnya, dan terima kasih atas tanggapan Anda karena saya pikir itu jelas dari kondisi saya yang jarang dan berani. Saya menganggap dosis skeptisisme yang sehat, tetapi lupa itu tidak jelas. Silakan lihat jawaban saya yang sudah direvisi, dan beri tahu saya cara meningkatkannya.
Jeff Puckett