แบบสอบถามของ 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 Injections และปัญหาอื่น ๆ

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);เพื่อเพิ่มความปลอดภัย?
แดน

ทำไมqueryกลับPDOStatementแทนบูลเช่นexecute?
ลีโอ

1
การทำซ้ำแบบสอบถามหลายครั้งไม่ได้ทำงานกับไดรเวอร์ PDO ทั้งหมด
Jeff Puckett

47

ไม่พวกเขาไม่เหมือนกัน นอกเหนือจากการหลีกเลี่ยงบนฝั่งไคลเอ็นต์ที่จัดเตรียมไว้คำสั่งที่เตรียมไว้จะถูกคอมไพล์บนฝั่งเซิร์ฟเวอร์หนึ่งครั้งจากนั้นสามารถส่งผ่านพารามิเตอร์ที่แตกต่างกันในแต่ละการดำเนินการ ซึ่งหมายความว่าคุณสามารถทำได้:

$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

เยี่ยมกับ MySQL แต่ไม่ได้ทำงานกับไดรเวอร์ PDO ทั้งหมด
Jeff Puckett

3

คำตอบของ Gileanดีมาก แต่ฉันแค่อยากจะเพิ่มว่าบางครั้งมีข้อยกเว้นที่หายากสำหรับแนวทางปฏิบัติที่ดีที่สุดและคุณอาจต้องการทดสอบสภาพแวดล้อมของคุณทั้งสองวิธีเพื่อดูว่าอะไรจะได้ผลดีที่สุด

ในกรณีหนึ่งผมพบว่าqueryทำงานได้เร็วขึ้นเพื่อวัตถุประสงค์ของฉันเพราะฉันเป็นจำนวนมากการถ่ายโอนข้อมูลที่เชื่อถือได้จากกล่อง Ubuntu Linux ทำงาน PHP7 กับการสนับสนุนไม่ดีควบคุม Microsoft ODBC สำหรับ MS SQL Server

ฉันมาถึงคำถามนี้เพราะฉันมีสคริปต์ที่ใช้งานยาวนานสำหรับETLซึ่งฉันพยายามบีบเพื่อความเร็ว ดูเหมือนจะใช้งานง่ายสำหรับฉันที่queryอาจเร็วกว่า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;
}

ฉันได้เล่นกับการทดลองและการนับที่แตกต่างกันหลายครั้งในสภาพแวดล้อมเฉพาะของฉันและได้รับผลลัพธ์ที่เร็วกว่า 20-30% อย่างต่อเนื่องโดยมีqueryมากกว่าprepare/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 เตรียมความพร้อม
(7) เตรียมโหวต: 6.0673267160143
(8) แบบสอบถามโหวต: 4.3276024162769

ฉันอยากรู้ว่าการทดสอบนี้เปรียบเทียบกับสภาพแวดล้อมอื่น ๆ เช่น MySQL อย่างไร


ปัญหาเกี่ยวกับ "หลักฐานเชิงประจักษ์" (หรือการทดสอบเทียม) ที่สะท้อนถึงเงื่อนไข (ไม่ทราบ) ของคุณโดยเฉพาะและอาจแตกต่างกันไปสำหรับใครก็ตามนับประสาอะไรกับหลักฐานเชิงประจักษ์ในโลกแห่งความเป็นจริง แต่บางคนจะยอมรับและเผยแพร่ต่อไป
สามัญสำนึกของคุณ

@YourCommonSense ฉันเห็นด้วยอย่างสมบูรณ์และขอขอบคุณสำหรับความคิดเห็นของคุณเพราะฉันคิดว่ามันชัดเจนจากเงื่อนไขที่หายากของฉัน ฉันคิดว่าเป็นคนขี้สงสัยในปริมาณที่ดี แต่อย่าลืมว่ามันไม่ชัดเจน โปรดดูคำตอบที่แก้ไขของฉันและแจ้งให้เราทราบว่าจะปรับปรุงได้อย่างไร
Jeff Puckett
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.