PDO'nun sorgusu ve yürütme


129

İkisi de aynı şeyi mi yapıyor, sadece farklı mı?

prepareArasında kullanmanın dışında herhangi bir fark var mı

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

ve

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

?

Yanıtlar:


145

query standart bir SQL deyimi çalıştırır ve SQL Enjeksiyonlarını ve diğer sorunları önlemek için tüm verilerden düzgün şekilde çıkış yapmanızı gerektirir.

executeparametrelerden çıkma veya alıntı yapma ihtiyacını ortadan kaldırmak için parametreleri bağlamanıza izin veren hazırlanmış bir deyim çalıştırır. executebir sorguyu birden çok kez tekrarlıyorsanız da daha iyi performans gösterir. Hazırlanan ifadelere örnek:

$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

En iyi uygulama, hazırlanmış ifadelere bağlı kalmak ve executedaha fazla güvenlik sağlamaktır .

Ayrıca bkz: PDO tarafından hazırlanan ifadeler SQL enjeksiyonunu önlemek için yeterli mi?


Bağlantı, yorumlarda zaten eleştirilen oldukça aptalca bir cevapla soruya götürüyor.
Sizin Common Sense

Öyleyse, bir hazırlık kullanıyorsanız : calories , mysql_real_escape_string()enjeksiyonları durdurmaya eşdeğer mi yoksa $sth->bindParam(':calories', $calories);güvenliği artırmaktan daha fazlasına mı ihtiyacınız var ?
Dan

Bool like yerine neden querybir PDOStatement döndürüyor ? execute
Leo

1
Bir sorguyu birden çok kez tekrarlamak tüm PDO sürücülerinde çalışmaz .
Jeff Puckett

47

Hayır, aynı değiller. Sağladığı istemci tarafındaki kaçış dışında, hazırlanmış bir ifade sunucu tarafında bir kez derlenir ve ardından her yürütmede farklı parametreler geçirilebilir . Bu, şunları yapabileceğiniz anlamına gelir:

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

Küçük ölçekte fark edilmese de genellikle size bir performans artışı sağlarlar. Hazırlanmış ifadeler (MySQL sürümü) hakkında daha fazla bilgi edinin .


Neden daha hızlı olacağını açıklama şeklini beğendim.
timfreilly


3

Gilean'ın cevabı harika, ancak bazen en iyi uygulamaların nadir istisnaları olduğunu ve en iyi neyin işe yarayacağını görmek için ortamınızı iki şekilde de test etmek isteyebileceğinizi eklemek istedim.

Bir durumda, MS SQL Server içinquery zayıf bir şekilde desteklenen Microsoft ODBC sürücüsü ile PHP7 çalıştıran bir Ubuntu Linux kutusundan güvenilir verileri toplu olarak transfer ettiğim için bunun amaçlarım için daha hızlı çalıştığını buldum .

Bu soruya ulaştım çünkü hız için sıkıştırmaya çalıştığım bir ETL için uzun süredir çalışan bir komut dosyam vardı. Bana hiç de sezgisel görünüyordu querydaha hızlı olabilir prepareve executebunun iki yerine tek işlevinin çağrılması çünkü. Parametre bağlama işlemi mükemmel koruma sağlar, ancak pahalı olabilir ve gereksizse önlenebilir.

Birkaç nadir durum göz önüne alındığında :

  1. Microsoft ODBC sürücüsü tarafından desteklenmediği için hazırlanmış bir deyimi yeniden kullanamıyorsanız .

  2. Girdiyi temizleme konusunda endişelenmiyorsanız ve basit kaçış kabul edilebilir. Bunun nedeni, belirli veri türlerinin bağlanmasının Microsoft ODBC sürücüsü tarafından desteklenmemesi olabilir .

  3. PDO::lastInsertId Microsoft ODBC sürücüsü tarafından desteklenmez.

İşte ortamımı test etmek için kullandığım bir yöntem ve umarım siz bunu çoğaltabilirsiniz veya sizinkinde daha iyi bir şey yapabilirsiniz:

Başlamak için Microsoft SQL Server'da temel bir tablo oluşturdum

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

Ve şimdi performans ölçümleri için temel bir zamanlanmış test.

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

Benim belirli bir ortamda birden fazla farklı deneme ve sayıları ile oynanan ve sürekli daha hızlı% 20-30 arası sonuçlar elde ettik querydaha prepare/execute

5,8128969669342 hazırlamak
5,8688418865204 hazırlamak
4,2948560714722 sorgu
4,9533629417419 sorgu
5,9051351547241 hazırlamak
4,332102060318 sorgu
5,9672858715057 hazırlamak
5,0667371749878 sorgu
3,8260300159454 sorgu
4,0791549682617 sorgu
4,3775160312653 sorgu
3,6910600662231 sorgu
hazırlamak 5,2708210945129
6,2671611309052 hazırlamak
7,3791449069977 hazırlamak
6,0673267160143: (7) ortalama hazırlanması
(8) sorgu Ortalama: 4,3276024162769

Bu testin MySQL gibi diğer ortamlarda nasıl karşılaştırıldığını merak ediyorum.


Sizin (bilinmeyen) özel koşullarınızı yansıtan "ampirik kanıtlar" (veya daha doğrusu yapay testler) ile ilgili sorun ve gerçek dünyadaki deneysel kanıtları bırakın, başkaları için farklılık gösterebilirler. Yine de bazı insanlar bunu hafife alacak ve kelimeyi daha da yayacak.
Sağduyu

@YourCommonSense Tamamen katılıyorum ve geri bildiriminiz için teşekkür ederim çünkü bunun cesur nadir koşullarımın açık olduğunu düşündüm. Sağlıklı dozda şüphecilik varsayıyorum, ancak bunun açık olmadığını unutuyorum. Lütfen düzeltilmiş cevabıma bakın ve nasıl geliştirilebileceğini bana bildirin.
Jeff Puckett
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.