Запит 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?


Посилання призводить до питання з досить дурною відповіддю, вже критикуваною в коментарях.
Твій здоровий глузд

Отже, якщо ви використовуєте : 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) .


Мені подобається, як ти пояснив, чому це буде швидше.
timfreilly


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 Я погоджуюся повністю, і дякую за ваш відгук, тому що я вважав, що це зрозуміло з моїх сміливих рідкісних умов. Я припускаю здорову дозу скептицизму, але забудьте, що це не очевидно. Будь ласка, дивіться мою переглянуту мою відповідь, і дайте мені знати, як її можна вдосконалити.
Джефф Пукетт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.