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

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

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

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

и

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

?

Ответ 1

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?

Ответ 2

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

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

Ответ 3

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

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

Я пришел к этому вопросу, потому что у меня был длинный script для ETL, который я пытался сжать для скорости. Мне показалось интуитивным, что query может быть быстрее, чем prepare и execute, потому что он вызывал только одну функцию вместо двух. Операция привязки параметров обеспечивает отличную защиту, но она может быть дорогостоящей и, возможно, избегать, если это необходимо.

Учитывая несколько редких условий:

Вот метод, который я использовал для проверки своей среды, и, надеюсь, вы можете воспроизвести его или что-то еще лучше:

Чтобы начать, я создал базовую таблицу в 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.