Porównywanie funkcji wykonywalnych

Pobieranie sterownika PHP

Oba sterowniki mogą wykonać instrukcję raz lub przygotować instrukcję do wielokrotnego wykonania. Używaj parametrów dla wartości dostarczonych przez użytkowników, nawet jeśli zapytanie wykonujesz tylko raz.

Wybierz API wykonawcze osobno od sposobu pobierania wyników. Przygotowanie zapytania nie pobiera jego wierszy, a buforowanie wyniku nie sprawia, że zapytanie staje się parametryzowane. Poniższe przykłady przygotowują zapytanie tylko do odczytu raz, wykonują je dwukrotnie z różnymi wartościami i konsumują każdy wynik przed kolejnym wykonaniem.

Styl wykonania SQLSRV PDO_SQLSRV
Uruchom zapytanie raz, bez parametrów sqlsrv_query PDO::query dla wyników lub PDO::exec dla liczby zmienionych wierszy
Uruchom zapytanie raz, z parametrami sqlsrv_query($conn, $sql, $params) PDO::prepare(), a następnie PDOStatement::execute()
Przygotuj raz, wykonaj powtarzalnie z różnymi parametrami sqlsrv_prepare + sqlsrv_execute PDO::prepare + PDOStatement::execute

sqlsrv_query() przygotowuje i wykonuje w jednym wywołaniu oraz zwraca zasób instrukcji. sqlsrv_prepare() zwraca zasób instrukcji bez jego wykonywania; wywołaj sqlsrv_execute() przy każdym wykonaniu. PDO wykonuje query() i zwraca PDOStatement, a exec() zamiast obiektu wynikowego zwraca liczbę wierszy dotkniętych. Udana exec() może zwrócić 0, więc porównaj zwrot niepowodzenia z === false, a nie if (!$count).

PDO::query() nie akceptuje tablicy wartości parametrów. Opcjonalne argumenty konfigurują pobieranie. Dla parametryzowanych zapytań PDO_SQLSRV używaj prepare() i execute().

Jednokrotne przygotowanie pozwala ponownie użyć uchwytu instrukcji SQL z innymi wartościami. To nie gwarantuje, że serwer nigdy nie przekompiluje swojego planu wykonawczego. PDO_SQLSRV domyślnie używa przygotowanego wykonania, w tym dla PDO::query(); atrybut direct-query kontroluje ten oddzielny wybór.

Przygotuj raz i wykonaj wielokrotnie

Są to oddzielne, kompletne programy konsolowe. Zainstaluj i załaduj wybrane rozszerzenie, ustaw SQL_SERVER, SQL_DATABASE, SQL_USER, i SQL_PASSWORD w środowisku procesu PHP. Użyj testowej bazy danych SQL Server oraz certyfikatu serwera, któremu klient ufa. Aby dowiedzieć się o konfiguracji środowiska i alternatywach bezhasłowych, zobacz Łączenie z serwerem.

Przechowuj rzeczywistą nazwę bazy danych, nazwę użytkownika i hasło w zmiennych środowiskowych. Oba programy jednokrotnie stosują sekwencje ucieczki w tych wartościach, podwajając zamykające nawiasy klamrowe i ujmując każdą wartość w nawiasy klamrowe, zgodnie z wymaganiami obsługi połączeń w stylu ODBC w sterowniku PHP. Escapowanie wartości połączenia jest oddzielne od powiązania parametrów zapytania.

Programy wykonują zapytanie Transact-SQL (T-SQL) na dwóch wbudowanych wierszach bez tworzenia tabeli czy zmiany danych. Zapisz każdy program w osobnym pliku .php i uruchom go za pomocą php <file>. Nie jest wymagana żadna przykładowa baza danych.

SQLSRV

Przekaż $productId przez referencję za pomocą &$productId. Każdy sqlsrv_execute() wykorzystuje wtedy swoją aktualną wartość. SQLSRV_FETCH_ASSOC zwraca tablicę z kluczami będącymi nazwami kolumn. Wynik false oznacza, że nie ma już więcej wierszy; null oznacza błąd.

<?php
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
    $value = getenv($name);
    if ($value === false || $value === '') {
        throw new RuntimeException("Set $name before running this example.");
    }
    $config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
    $config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$conn = sqlsrv_connect($config['SQL_SERVER'], [
    'Database' => $config['SQL_DATABASE'],
    'UID' => $config['SQL_USER'],
    'PWD' => $config['SQL_PASSWORD'],
    'Encrypt' => true,
    'TrustServerCertificate' => false,
    'LoginTimeout' => 30,
    'CharacterSet' => 'UTF-8',
]);
if ($conn === false) {
    throw new RuntimeException(print_r(sqlsrv_errors(), true));
}

$sql = "SELECT Name FROM (VALUES (1, 'Bike'), (2, 'Helmet')) AS Product(ProductId, Name) WHERE ProductId = ?";
$productId = 1;
$stmt = null;
try {
    $stmt = sqlsrv_prepare($conn, $sql, [&$productId], ['QueryTimeout' => 30]);
    if ($stmt === false) {
        throw new RuntimeException(print_r(sqlsrv_errors(), true));
    }
    foreach ([1 => 'Bike', 2 => 'Helmet'] as $productId => $expected) {
        if (!sqlsrv_execute($stmt)) {
            throw new RuntimeException(print_r(sqlsrv_errors(), true));
        }
        $names = [];
        while (($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) !== null) {
            if ($row === false) {
                throw new RuntimeException(print_r(sqlsrv_errors(), true));
            }
            $names[] = $row['Name'];
        }
        if ($names !== [$expected]) {
            throw new RuntimeException('Expected exactly one matching product.');
        }
        echo $names[0], PHP_EOL;
    }
} finally {
    if (is_resource($stmt)) {
        sqlsrv_free_stmt($stmt);
    }
    sqlsrv_close($conn);
}

PDO_SQLSRV

Zwiąż $productId raz z bindParam(). Każdy execute() odczytuje swoją aktualną wartość. PDO::PARAM_INT określa typ wiązania PHP/PDO parametru; nie kontroluje typu kolumn PHP pobieranych z wyniku. Ustaw tryb wyjątku tak, aby błąd bazy danych przerwał wykonywanie, zamiast być mylnie uznany za pusty wynik.

<?php
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
    $value = getenv($name);
    if ($value === false || $value === '') {
        throw new RuntimeException("Set $name before running this example.");
    }
    $config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
    $config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$server = $config['SQL_SERVER'];
$database = $config['SQL_DATABASE'];
$pdo = new PDO(
    "sqlsrv:Server=$server;Database=$database;Encrypt=true;"
    . "TrustServerCertificate=false;LoginTimeout=30",
    $config['SQL_USER'],
    $config['SQL_PASSWORD'],
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8,
    ]
);
$sql = "SELECT Name FROM (VALUES (1, 'Bike'), (2, 'Helmet')) AS Product(ProductId, Name) WHERE ProductId = ?";
$stmt = null;
try {
    $stmt = $pdo->prepare($sql, [PDO::SQLSRV_ATTR_QUERY_TIMEOUT => 30]);
    $productId = 1;
    $stmt->bindParam(1, $productId, PDO::PARAM_INT);
    foreach ([1 => 'Bike', 2 => 'Helmet'] as $productId => $expected) {
        $stmt->execute();
        $names = $stmt->fetchAll(PDO::FETCH_COLUMN);
        if ($names !== [$expected]) {
            throw new RuntimeException('Expected exactly one matching product.');
        }
        echo $names[0], PHP_EOL;
        $stmt->closeCursor();
    }
} finally {
    $stmt = null;
    $pdo = null;
}

Każdy przykład drukuje:

Bike
Helmet

Wartości w tablicy przekazywanej do execute() PDO traktowane są jako PDO::PARAM_STR. Gdy SQL wymaga parametru całkowitoliczbowego, takiego jak parametryzowana TOP liczba, wywołaj bindValue() z i PDO::PARAM_INT wywołaj execute() bez tablicy.

Przypisuj wartości bez zmiany zapytania

Znacznik parametru reprezentuje wartość, a nie nazwę tabeli, nazwę kolumny, słowo kluczowe ani kierunek sortowania. Nie stawiaj cudzysłowu wokół ?. Dla identyfikatora dynamicznego lub ASC/DESC wybierz z kontrolowanej przez aplikację listy dozwolonych elementów zamiast łączenia niezaufanego tekstu. Dla listy IN podaj jeden znacznik na każdą wartość.

Requirement SQLSRV PDO_SQLSRV
Powiąż wartości dla jednego wykonania Przekaż tablicę parametrów do sqlsrv_query(). Przekaż tablicę do execute() po prepare(), lub najpierw przypisz wartości.
Ponownie wykorzystaj aktualną wartość zmiennej Uwzględnij &$variable w tablicy podanej do sqlsrv_prepare(). Wywołaj bindParam() ze zmienną, a następnie execute() bez tablicy. PHP przekazuje zmienną według referencji; nie dodawaj & na stronie wywołania.
Przypisz wartość w momencie wiązania Użyj wartości w tablicy parametrów do wykonania. Wywołaj bindValue(); późniejsze zmiany w oryginalnej zmiennej nie zmieniają tej dowiązanej wartości.
Określ wejście całkowitoliczbowe Użyj deskryptora parametru z SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_INT i SQLSRV_SQLTYPE_INT, gdy potrzebny jest typ jawny. Zwiąż z PDO::PARAM_INT. Indeksy pozycyjne bindParam() i bindValue() zaczynają się od 1, a nie 0.
Wyślij SQL NULL Powiąż PHP null, z wyraźnym typem SQL, gdy operacja tego wymaga. Zwiąż z PDO::PARAM_NULL.
Odbierz parametry wyjściowe Użyj zmiennej referencyjnej z SQLSRV_PARAM_OUT lub SQLSRV_PARAM_INOUT. Używaj bindParam() z PDO::PARAM_INPUT_OUTPUT oraz wymaganym typem i długością.

Utrzymuj zmienne z odniesieniami aktywne podczas wykonywania prepared statement. Program SQLSRV zmienia związaną zmienną, zamiast zastępować tablicę parametrów. Program PDO celowo wywołuje execute() bez tablicy po zbindowaniu parametrów; nie mieszaj tego wzorca z wykonywaniem opartym na tablicy.

Wartości wyjściowe z procedur przechowywanych wymagają własnej obsługi, w tym obsługi wyników procedury. Nie wyciągaj wniosków o zachowaniu parametrów wyjściowych na podstawie tych przykładów tylko wejściowych. Więcej informacji można znaleźć w artykule Używanie parametrów kierunkowych.

Konfiguruj zachowanie wykonania i pobierania

Setting SQLSRV PDO_SQLSRV
Czas wykonania QueryTimeout w tablicy opcji deklaracji. PDO::SQLSRV_ATTR_QUERY_TIMEOUT na połączeniu lub instrukcji.
Nazwane klucze wyników SQLSRV_FETCH_ASSOC przekazano do sqlsrv_fetch_array(). PDO::FETCH_ASSOC przekazywano do metody pobierania lub ustawiano jako domyślny tryb pobierania połączenia.
Wyniki tylko do przodu Domyślny kursor; pobieraj w kolejności. Domyślny kursor; pobieranie w kolejności.
Wyniki buforowane przez klienta Scrollable => SQLSRV_CURSOR_CLIENT_BUFFERED. PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL za pomocą PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED.
Bezpośrednie a przygotowane wykonanie Brak atrybutu PDO direct-query. Wybierz połączone sqlsrv_query() wywołanie lub jawną parę przygotowanie/wykonanie. PDO::SQLSRV_ATTR_DIRECT_QUERY wybiera wykonanie bezpośrednie zamiast domyślnego wykonania przygotowanego przez sterownik.
Emulacja parametrów Brak atrybutu emulacji PDO. PDO::ATTR_EMULATE_PREPARES jest oddzielna od atrybutu zapytania bezpośredniego. Nie używaj go jako substytutu wiązania odpowiednich typów.

Przykłady dają niewielkie wyniki. fetchAll() ładuje każdy wiersz do pamięci PHP; używaj pętli pobierania do dużego wyniku. Zapytanie z brakiem dopasowań nie jest błędem wykonania. SQLSRV raportuje koniec wyniku jako null z sqlsrv_fetch_array(); PDO raportuje go jako false z fetch(). Pewien element SQL NULL w pobranym wierszu pozostaje PHP null.

Nie używaj liczby wierszy SELECT jako jedynego testu sukcesu. Kursory tylko do przodu nie oferują takich samych możliwości liczenia wierszy jak kursory buforowane czy przewijane. Pobierz i policz potrzebne wiersze lub użyj osobnego COUNT zapytania. Aby uzyskać opcje i ograniczenia specyficzne dla kursora, zobacz Określ typ kursora i wybierz wiersze.

Obsługuj błędy i końcowe wyniki

Sprawdź operacje SQLSRV prepare, execute i fetch oddzielnie. Pomyślne przygotowanie instrukcji nie dowodzi, że wykonanie się powiedzie: serwer może odrzucić wartości parametrów, odmówić wykonania z powodu niewystarczających uprawnień lub odrzucić polecenie podczas wykonywania. Przechwyć sqlsrv_errors() natychmiast po operacji zwracającej false. SQLSRV domyślnie traktuje wiele ostrzeżeń jako błędy, więc przeglądaj całą kolekcję.

W przypadku PDO, w trybie zgłaszania wyjątków, awarie połączenia i błędy bazy danych powodują zgłoszenie wyjątku PDOException. Sprawdź jego SQLSTATE i informacje o błędach sterownika w chronionej diagnostyce aplikacji; nie zwracaj tekstu wyjątku ani poświadczeń w odpowiedzi HTTP. prepare() Sukces nie jest dowodem na to, że oświadczenie już zostało opublikowane.

Objaw Action
Każde wykonanie używa pierwszej wartości Dla SQLSRV, powiąż zmienną przez referencję. Dla PDO używaj bindParam() zmiennej zmieniającej się lub wywołuj bindValue() każdą nową wartość.
Odrzucany jest parametr numeryczny, taki jak TOP liczba Przypisz wymagany typ liczby całkowitej wyraźnie. PDO execute([$value]) używa parametrów ciągu nawet jeśli wartość PHP jest liczbą całkowitą.
Drugie dowództwo raportuje oczekujące wyniki Przetwórz wyniki lub je udostępnij przed ponownym użyciem połączenia. W przypadku PDO używaj closeCursor() , gdy nie potrzebujesz pozostałego efektu.
Oczekiwane parametry wyjściowe nie uległy zmianie Uzupełnij wszystkie wyniki procedur i zweryfikuj kierunek parametrów, typ PHP oraz długość bufora wyjściowego.
Zużycie pamięci rośnie wraz z rozmiarem wyniku Zastąp fetchAll() lub niepotrzebne buforowanie klienta sekwencyjnym pobieraniem.

Przykłady zwalniają swoje instrukcje i zwalniają swoje połączenia w finally. Nie rozpoczynają transakcji. Użyj jawnej transakcji , gdy wiele zapisów musi się zatwierdzać razem lub wszystkie cofnąć. Ponowne użycie przygotowanej instrukcji nie grupuje zapisów w transakcję.

Różnice w celu i implementacji

API przygotowując/wykonujące oraz reguły referencyjne PHP należą do rozszerzeń oraz PHP/PDO. Obiekt docelowy SQL decyduje, które instrukcje i typy T-SQL są obsługiwane, a ODBC realizuje komunikację niskiego poziomu. Jednorazowe przygotowanie może zmniejszyć powtarzalną pracę, ale nie gwarantuje konkretnego planu uruchomienia serwera ani przyspieszenia.

Wzorce wiązania danych wejściowych dotyczą SQL Server, Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics, bazy danych SQL w Microsoft Fabric oraz Warehouse w Microsoft Fabric. Programy te wykorzystują uwierzytelnianie SQL do SQL Server. Podczas wdrażania usługi Azure wybierz uwierzytelnianie Microsoft Entra. Oba produkty Fabric tego wymagają, a Warehouse wymaga także wyłączenia Multiple Active Result Sets (MARS). Sprawdź wsparcie wybranego celu dla zmian schematów, transakcji i procedur przechowywanych przed ponownym użyciem kodu SQL Server. Różnice między punktami końcowymi a uwierzytelnianiem można znaleźć w Azure i Fabric connections.