Запрос 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 и других проблем.

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?

Gilean
источник
Ссылка ведет к вопросу с довольно глупым ответом, уже раскритикованным в комментариях.
Your Common Sense
Итак, если вы используете : calories препарат, это эквивалент mysql_real_escape_string()остановки инъекций, или вам нужно больше, чем просто $sth->bindParam(':calories', $calories);для повышения безопасности?
Дэн
Почему queryвозвращает PDOStatement , вместо BOOL , как execute?
Лев,
1
Многократное повторение запроса работает не со всеми драйверами PDO .
Джефф Пакетт,
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) .

netcoder
источник
Мне нравится, как вы объяснили, почему это будет быстрее.
timfreilly
Отлично работает с MySQL, но не работает со всеми драйверами PDO .
Джефф Пакетт,
3

Ответ Гилеан великолепен, но я просто хотел добавить, что иногда бывают редкие исключения из лучших практик, и вы можете протестировать свою среду в обоих направлениях, чтобы увидеть, что будет работать лучше всего.

В одном случае я обнаружил, что это 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 Я полностью согласен и спасибо за ваш отзыв, потому что я думал, что это ясно из моих смелых редких условий. Я допускаю здоровую дозу скептицизма, но забываю, что это не очевидно. Пожалуйста, посмотрите мой измененный ответ и дайте мне знать, как его можно улучшить.
Джефф Пакетт,