Zapytanie PDO vs wykonanie

130

Czy obaj robią to samo, tylko inaczej?

Czy jest jakaś różnica oprócz używania preparemiędzy

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

i

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

?

Qiao
źródło

Odpowiedzi:

145

query uruchamia standardową instrukcję SQL i wymaga poprawnej zmiany znaczenia wszystkich danych w celu uniknięcia wstrzyknięć SQL i innych problemów.

executeuruchamia przygotowaną instrukcję, która pozwala powiązać parametry, aby uniknąć konieczności ucieczki lub cytowania parametrów. executebędzie również działać lepiej, jeśli będziesz powtarzać zapytanie wiele razy. Przykładowe przygotowane zestawienia:

$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

Najlepszą praktyką jest trzymanie się przygotowanych oświadczeń i executezwiększenie bezpieczeństwa .

Zobacz też: Czy przygotowane instrukcje PDO wystarczą, aby zapobiec wstrzykiwaniu SQL?

Gilean
źródło
Link prowadzi do pytania z dość głupią odpowiedzią, krytykowanego już w komentarzach.
Your Common Sense
Więc jeśli używasz : calories preparatu na to, czy jest to odpowiednik mysql_real_escape_string()zatrzymania zastrzyków, czy potrzebujesz czegoś więcej niż tylko $sth->bindParam(':calories', $calories);zwiększenia bezpieczeństwa?
Dan,
Dlaczego queryzwrócić PDOStatement , zamiast bool podobnego execute?
Leo
1
Wielokrotne powtarzanie zapytania nie działa ze wszystkimi sterownikami PDO .
Jeff Puckett,
47

Nie, to nie to samo. Oprócz funkcji ucieczki po stronie klienta, którą dostarcza, przygotowana instrukcja jest kompilowana raz po stronie serwera, a następnie może być przekazywana innym parametrom przy każdym wykonaniu. Co oznacza, że ​​możesz:

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

Zwykle poprawiają wydajność, chociaż nie są zauważalne na małą skalę. Przeczytaj więcej o przygotowanych wyciągach (wersja MySQL) .

netcoder
źródło
Podoba mi się sposób, w jaki wyjaśniłeś, dlaczego byłoby to szybsze.
timfreilly
Fantastyczny z MySQL, ale nie działa ze wszystkimi sterownikami PDO .
Jeff Puckett,
3

Odpowiedź Gileana jest świetna, ale chciałem tylko dodać, że czasami zdarzają się rzadkie wyjątki od najlepszych praktyk i możesz chcieć przetestować swoje środowisko w obie strony, aby zobaczyć, co będzie działać najlepiej.

W jednym przypadku okazało się, że querydziała to szybciej dla moich celów, ponieważ masowo przesyłałem zaufane dane z komputera z systemem Ubuntu Linux z PHP7 ze słabo obsługiwanym sterownikiem Microsoft ODBC dla MS SQL Server .

Dotarłem do tego pytania, ponieważ miałem długo działający skrypt dla ETL, który próbowałem wycisnąć dla szybkości. Wydawało mi się intuicyjne, że querymoże być szybsze niż prepare&, executeponieważ wywoływało tylko jedną funkcję zamiast dwóch. Operacja wiązania parametrów zapewnia doskonałą ochronę, ale może być kosztowna i prawdopodobnie można jej uniknąć, jeśli jest niepotrzebna.

Biorąc pod uwagę kilka rzadkich warunków :

  1. Jeśli nie możesz ponownie użyć przygotowanej instrukcji, ponieważ nie jest ona obsługiwana przez sterownik Microsoft ODBC .

  2. Jeśli nie martwisz się o odkażanie danych wejściowych i zwykła ucieczka jest do przyjęcia. Może tak być, ponieważ wiązanie pewnych typów danych nie jest obsługiwane przez sterownik Microsoft ODBC .

  3. PDO::lastInsertId nie jest obsługiwany przez sterownik Microsoft ODBC.

Oto metoda, której użyłem do przetestowania mojego środowiska i miejmy nadzieję, że możesz ją odtworzyć lub coś lepszego w swoim:

Na początek utworzyłem podstawową tabelę w Microsoft SQL Server

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

A teraz podstawowy test czasowy dla wskaźników wydajności.

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

Grałem z wieloma różnymi próbami i liczyłem w moim specyficznym środowisku i konsekwentnie uzyskuję od 20-30% szybszych wyników z queryniż prepare/execute

5,8128969669342 przygotować
5,8688418865204 przygotować
4,2948560714722 zapytanie
4,9533629417419 zapytanie
5,9051351547241 przygotować
4.332102060318 zapytanie
5,9672858715057 przygotować
5.0667371749878 zapytanie
3,8260300159454 zapytania
4,0791549682617 zapytanie
4,3775160312653 zapytanie
3,6910600662231 zapytanie
5,2708210945129 przygotować
6,2671611309052 przygotować
7,3791449069977 przygotowania
(7) przygotowuje Średnia: 6.0673267160143
(8) zapytania Średnia: 4,3276024162769

Jestem ciekawy, jak ten test wypada w porównaniu z innymi środowiskami, takimi jak MySQL.

Jeff Puckett
źródło
Problem z „empirycznymi dowodami” (lub raczej sztucznymi testami), że odzwierciedlają one twoje (nieznane) szczególne warunki i mogą różnić się dla każdego innego, nie mówiąc już o empirycznych dowodach ze świata rzeczywistego. Jednak niektórzy uznają to za coś oczywistego i będą dalej rozpowszechniać tę informację.
Twój zdrowy rozsądek
@YourCommonSense Zgadzam się całkowicie i dziękuję za twoją opinię, ponieważ myślałem, że wynika to z moich śmiałych rzadkich warunków. Zakładam sporą dawkę sceptycyzmu, ale zapominam, że nie jest to oczywiste. Zobacz moją poprawioną odpowiedź i daj mi znać, jak można ją ulepszyć.
Jeff Puckett