Запрос PDO против выполнения


Они оба делают то же самое, только по-другому?

есть ли разница помимо использования prepare между

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

и

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

?

3 117

3 ответа:

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 для повышения безопасности.

Читайте также: являются ПДО подготовленных заявлений достаточно, чтобы предотвратить SQL-инъекции?

нет, это не одно и то же. Помимо побега на стороне клиента, которую он предоставляет,подготовленный оператор компилируется на стороне сервера один раз, а затем могут быть переданы различные параметры при каждом исполнении. Что означает, что вы можете сделать:

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

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

в одном случае, я обнаружил, что query работал быстрее для моих целей, потому что я массово передавал доверенные данные из Ubuntu Linux box под управлением PHP7 с плохо поддерживаемым драйвер Microsoft ODBC для MS SQL Server.

I пришел к этому вопросу, потому что у меня был длинный скрипт для 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.