Truy vấn của PDO vs thực thi


129

Có phải cả hai đều làm cùng một việc, chỉ khác nhau?

Có sự khác biệt nào ngoài việc sử dụng preparegiữa

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

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

?

Câu trả lời:


145

query chạy một câu lệnh SQL tiêu chuẩn và yêu cầu bạn thoát đúng tất cả dữ liệu để tránh Tiêm SQL và các vấn đề khác.

executechạy một câu lệnh được chuẩn bị cho phép bạn liên kết các tham số để tránh phải thoát hoặc trích dẫn các tham số. executecũng sẽ hoạt động tốt hơn nếu bạn lặp lại một truy vấn nhiều lần. Ví dụ về các tuyên bố đã chuẩn bị:

$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

Thực hành tốt nhất là gắn bó với các tuyên bố đã chuẩn bị và executeđể tăng tính bảo mật .

Xem thêm: Các tuyên bố chuẩn bị PDO có đủ để ngăn chặn việc tiêm SQL không?


Liên kết dẫn đến câu hỏi với câu trả lời khá ngu ngốc, đã bị chỉ trích trong các bình luận.
Ý thức chung của bạn

Vì vậy, nếu bạn sử dụng một chế phẩm trên : calories đó là loại tương đương mysql_real_escape_string()để ngừng tiêm hoặc bạn cần nhiều hơn là chỉ $sth->bindParam(':calories', $calories);để tăng cường an ninh?
Dan

Tại sao querytrả về PDOStatement , thay vì bool như thế executenào?
Leo


47

Không, chúng không giống nhau. Ngoài việc thoát ra ở phía máy khách mà nó cung cấp, một câu lệnh được chuẩn bị được biên dịch ở phía máy chủ một lần, và sau đó có thể được truyền các tham số khác nhau ở mỗi lần thực hiện. Điều đó có nghĩa là bạn có thể làm:

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

Họ thường sẽ cung cấp cho bạn một cải tiến hiệu suất, mặc dù không đáng chú ý trên quy mô nhỏ. Đọc thêm về các tuyên bố đã chuẩn bị (phiên bản MySQL) .


Tôi thích cách bạn giải thích tại sao nó sẽ nhanh hơn.
timfreilly


3

Câu trả lời của Gilean rất hay, nhưng tôi chỉ muốn nói thêm rằng đôi khi có những trường hợp ngoại lệ hiếm hoi đối với các thực tiễn tốt nhất và bạn có thể muốn kiểm tra môi trường của mình theo cả hai cách để xem cái gì sẽ hoạt động tốt nhất.

Trong một trường hợp, tôi thấy rằng nó queryhoạt động nhanh hơn cho mục đích của mình vì tôi đã chuyển số lượng lớn dữ liệu đáng tin cậy từ hộp Ubuntu Linux chạy PHP7 với trình điều khiển Microsoft ODBC được hỗ trợ kém cho MS SQL Server .

Tôi đã đến câu hỏi này bởi vì tôi có một kịch bản chạy dài cho một ETL mà tôi đang cố gắng để đạt được tốc độ. Nó dường như trực quan với tôi querycó thể nhanh hơn prepare& executebởi vì nó chỉ gọi một chức năng thay vì hai chức năng. Hoạt động liên kết tham số cung cấp bảo vệ tuyệt vời, nhưng nó có thể tốn kém và có thể tránh được nếu không cần thiết.

Đưa ra một vài điều kiện hiếm gặp :

  1. Nếu bạn không thể sử dụng lại câu lệnh đã chuẩn bị vì nó không được trình điều khiển Microsoft ODBC hỗ trợ .

  2. Nếu bạn không lo lắng về việc vệ sinh đầu vào và thoát đơn giản thì có thể chấp nhận được. Đây có thể là trường hợp vì ràng buộc một số kiểu dữ liệu nhất định không được trình điều khiển Microsoft ODBC hỗ trợ .

  3. PDO::lastInsertId không được trình điều khiển Microsoft ODBC hỗ trợ.

Đây là một phương pháp tôi đã sử dụng để kiểm tra môi trường của mình và hy vọng bạn có thể sao chép nó hoặc một cái gì đó tốt hơn trong môi trường của bạn:

Để bắt đầu, tôi đã tạo một bảng cơ bản trong Microsoft SQL Server

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

Và bây giờ là một bài kiểm tra thời gian cơ bản cho các số liệu hiệu suất.

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

Tôi đã chơi với nhiều thử nghiệm và số lượng khác nhau trong môi trường cụ thể của tôi, và liên tục nhận được từ 20-30% nhanh hơn kết quả với queryhơn prepare/execute

5,8128969669342 chuẩn bị
5,8688418865204 chuẩn bị
4,2948560714722 truy vấn
4,9533629417419 truy vấn
5,9051351547241 chuẩn bị
4,332102060318 truy vấn
5,9672858715057 chuẩn bị
5,0667371749878 truy vấn
3,8260300159454 truy vấn
4,0791549682617 truy vấn
4,3775160312653 truy vấn
3,6910600662231 truy vấn
5,2708210945129 chuẩn bị
6,2671611309052 chuẩn bị
7,3791449069977 chuẩn bị
(7) chuẩn bị trung bình: 6,0673267160143
(8) truy vấn trung bình: 4,3276024162769

Tôi tò mò muốn xem thử nghiệm này so sánh như thế nào trong các môi trường khác, như MySQL.


Vấn đề với "bằng chứng thực nghiệm" (hay đúng hơn là các xét nghiệm nhân tạo) mà chúng phản ánh các điều kiện cụ thể (chưa biết) của bạn và có thể khác với bất kỳ ai khác, chứ đừng nói đến bằng chứng thực nghiệm trong thế giới thực. Tuy nhiên, một số người sẽ coi đó là điều hiển nhiên và truyền bá hơn nữa.
Ý thức chung của bạn

@YourCommonSense Tôi hoàn toàn đồng ý và cảm ơn phản hồi của bạn vì tôi nghĩ điều đó rõ ràng từ những điều kiện hiếm hoi táo bạo của tôi. Tôi cho rằng một sự hoài nghi lành mạnh, nhưng quên nó không rõ ràng. Xin vui lòng xem sửa đổi câu trả lời của tôi, và cho tôi biết làm thế nào nó có thể được cải thiện.
Jeff Puckett
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.