Сравнение функций выполнения

Скачать драйвер PHP

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

Выберите API выполнения отдельно от того, как вы получаете результаты. Подготовка запроса не загружает его строки, а буферизация результата не делает запрос параметризованным. В следующих примерах можно подготовить запрос только для чтения один раз, выполнить его дважды с разными значениями и потреблять каждый результат перед следующим выполнением.

Стиль выполнения SQLSRV PDO_SQLSRV
Выполните запрос один раз, без параметров sqlsrv_query PDO::query для результатов или PDO::exec для подсчета затронутых строк
Выполните запрос один раз с параметрами sqlsrv_query($conn, $sql, $params) PDO::prepare(), затем PDOStatement::execute()
Подготовьте один раз, выполните многократно с разными параметрами sqlsrv_prepare + sqlsrv_execute PDO::prepare + PDOStatement::execute

sqlsrv_query() подготовляет и выполняет в одном вызове и возвращает ресурс выражения. sqlsrv_prepare() возвращает ресурс инструкции без его выполнения; вызывайте sqlsrv_execute() при каждом выполнении. PDO query() выполняется и возвращает PDOStatement, тогда как exec() возвращает количество затронутых строк вместо объекта результата. Успешный exec() может возвращать 0, поэтому сравните возвращаемое значение при ошибке с === false, а не if (!$count).

PDO::query() не принимает массив значений параметров. Его необязательные аргументы настраивают извлечение данных. Для параметризованных PDO_SQLSRV запросов используйте prepare() и execute().

Однократная подготовка позволяет повторно использовать дескриптор инструкции с различными значениями. Это не гарантирует, что сервер никогда не перекомпилирует свой план выполнения. PDO_SQLSRV по умолчанию использует подготовленное выполнение, включая для PDO::query(); его атрибут прямого запроса управляет этим отдельным выбором.

Подготовьтесь один раз и выполняйте многократно

Это отдельные, полноценные программы командной строки. Установите и загрузите выбранное расширение, а затем задайте SQL_SERVER, SQL_DATABASE, SQL_USER и SQL_PASSWORD в переменных окружения процесса PHP. Используйте тестовую базу данных SQL Server и серверный сертификат, которому доверяет клиент. Для настройки среды и альтернатив без пароля см. раздел «Подключение к серверу».

Храните фактическое имя базы данных, имя пользователя и пароль в переменных среды. Обе программы экранируют эти значения один раз, удваивая закрывающие скобки и заключая каждое значение в скобки, как того требует обработка ODBC-подключения драйвера PHP. Экранирование значений подключения выполняется отдельно от привязки параметров запроса.

Программы выполняют запрос Transact-SQL (T-SQL) для двух встроенных строк значений без создания таблицы и изменения данных. Сохраните каждую программу в отдельном .php файле и запустите её с php <file>. Тестовая база данных не требуется.

SQLSRV

Передайте $productId по ссылке с помощью &$productId. Каждый sqlsrv_execute() затем использует своё текущее значение. SQLSRV_FETCH_ASSOC Возвращает массив с ключом по имени столбца. Результат false означает, что строк больше нет; null указывает на ошибку.

<?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

Свяжите $productId один раз с bindParam(). Каждый execute() из них показывает своё текущее значение. PDO::PARAM_INT задаёт тип привязки параметра PHP/PDO; он не управляет типом PHP столбцов, полученных из результата. Настройте режим исключения так, чтобы ошибка базы данных прервала выполнение, а не ошибочно принималась за пустой результат.

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

Каждый пример выводит:

Bike
Helmet

Значения в массиве, передаваемом в execute() PDO, рассматриваются как PDO::PARAM_STR. Когда SQL требует целочисленного параметра, например параметризованного TOP счёта, вызовите bindValue() с PDO::PARAM_INT и затем вызовите execute() без массива.

Привязать значения без изменения запроса

Маркер параметра представляет значение, а не название таблицы, название столбца, ключевое слово или направление сортировки. Не ставьте кавычки вокруг ?. Для динамического идентификатора или ASC/DESC, выберите из списка разрешений, управляемого приложением, вместо конкатенации недоверенного текста. Для списка указывайте IN по одному маркеру на каждое значение.

Requirement SQLSRV PDO_SQLSRV
Значения привязки для одного выполнения Передайте массив параметров в sqlsrv_query(). Передайте массив в execute() после вызова prepare(), или сначала свяжите значения.
Повторно использовать текущее значение переменной Включите &$variable в массив, переданный в sqlsrv_prepare(). Вызовите bindParam() с переменной, затем execute() без массива. PHP передаёт переменную по ссылке; не добавляйте & в месте вызова.
Привязать значение в момент привязки Используйте значение в массиве параметров для выполнения. Вызовите bindValue(); последующие изменения исходной переменной не изменяют это связанное значение.
Укажите целочисленное входное значение Используйте дескриптор параметров с SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_INTи SQLSRV_SQLTYPE_INT когда требуется явный тип. Связать с PDO::PARAM_INT. Позиционные индексы bindParam() и bindValue() начинаются с 1, а не 0.
Отправить SQL NULL Привязать с помощью PHP null, с явным типом SQL, если этого требует операция. Связать с PDO::PARAM_NULL.
Получить выходные параметры Используйте ссылочную переменную с SQLSRV_PARAM_OUT или SQLSRV_PARAM_INOUT. Используйте bindParam() с PDO::PARAM_INPUT_OUTPUT и необходимыми типом и длиной.

Сохраняйте переменные, на которые есть ссылки, при выполнении подготовленного оператора. Программа SQLSRV меняет связанную переменную, а не заменяет массив параметров. Программа PDO после привязки сознательно вызывает execute() без массива; не смешивайте этот паттерн с выполнением на основе массивов.

Выходные значения из хранящихся процедур требуют собственной обработки, включая обработку всех наборов результатов процедуры. Не делайте выводы о поведении выходных параметров из этих примеров, относящихся только к входным параметрам. Для получения дополнительной информации см. раздел «Использование направленных параметров».

Настроить выполнение и поведение при извлечении

Setting SQLSRV PDO_SQLSRV
Тайм-аут выполнения QueryTimeout в массиве параметров инструкции. PDO::SQLSRV_ATTR_QUERY_TIMEOUT для соединения или оператора.
Именованные ключи результатов SQLSRV_FETCH_ASSOC передаётся в sqlsrv_fetch_array(). PDO::FETCH_ASSOC передаётся в метод выборки или устанавливается как стандартный режим выборки соединения.
Результаты только для прямого прохода Курсор по умолчанию; последовательно извлекать. Курсор по умолчанию; извлекать по порядку.
Результаты, буферированные клиентом Scrollable => SQLSRV_CURSOR_CLIENT_BUFFERED. PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL с PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED.
Прямое против подготовленного исполнения Нет атрибута прямого запроса PDO. Выберите комбинированный sqlsrv_query() вызов или явную пару «подготовить/выполнить». PDO::SQLSRV_ATTR_DIRECT_QUERY выбирает прямое выполнение вместо подготовленного выполнения по умолчанию, используемого драйвером.
Эмуляция параметров Нет атрибута эмуляции PDO. PDO::ATTR_EMULATE_PREPARES является отдельным от атрибута direct-query. Не используйте его как замену привязки правильных типов.

Примеры возвращают небольшие результаты. fetchAll() загружает каждую строку в память PHP; используйте цикл выборки для большого набора результатов. Запрос, не возвращающий совпадения, не является ошибкой выполнения. SQLSRV сообщает окончание набора результатов как null из sqlsrv_fetch_array(); PDO сообщает как false из fetch(). Значение SQL NULL в извлечённой строке остаётся PHP null.

Не используйте SELECT количество строк как единственную проверку успеха. Курсоры только вперёд не обеспечивают таких же возможностей подсчёта строк, как буферные или прокручиваемые курсоры. Получите и посчитайте нужные строки или используйте отдельный COUNT запрос. Для опций и ограничений, специфичных для курсора, см. Укажите тип курсора и выберите строки.

Обрабатывать сбои и результаты завершения

Проверьте операции SQLSRV подготовки, выполнения и извлечения данных по отдельности. Успешная подготовка не доказывает, что выполнение будет успешным: сервер может отклонять значения параметров, права доступа или оператор во время выполнения. Захватите sqlsrv_errors() сразу, когда операция возвращает false. SQLSRV по умолчанию рассматривает многие предупреждения как ошибки, поэтому проверьте всю коллекцию.

Для PDO сбои соединения и ошибки базы данных в режиме исключения выдают PDOException. Проверьте информацию об ошибках SQLSTATE и драйвера в защищённой диагностике приложений; не возвращайте текст исключений или учетные данные в HTTP-ответе. prepare() Успех не является доказательством того, что инструкция уже выполнена.

Симптом Action
Каждое выполнение использует первое значение Для SQLSRV привязывайте переменную по ссылке. Для PDO используйте bindParam() для меняющейся переменной или вызовите bindValue() для каждого нового значения.
Отклоняется числовой параметр, например количество TOP Явно привяжите требуемый целочисленный тип. PDO execute([$value]) использует параметры строк, даже если значение PHP является целым числом.
Вторая команда сообщает об ожидающих результатах Обработайте результаты или освободите их перед повторным использованием соединения. Для PDO используйте closeCursor(), когда не нужен оставшийся результат.
Ожидаемые параметры выхода не изменились Обработайте все результаты процедуры и проверьте направление параметров, тип PHP и длину выходного буфера.
Использование памяти увеличивается с размером результата Заменить fetchAll() или ненужное буферизование клиента последовательным получением.

Примеры освобождают свои операторы и закрывают свои соединения в finally. Они не начинают транзакцию. Используйте явную транзакцию, когда несколько операций записи должны либо зафиксироваться вместе, либо все откатиться. Повторное использование подготовленного оператора не группирует записи в транзакцию.

Различия между назначением и реализацией

API подготовки/выполнения и правила PHP-ссылки принадлежат расширениям и PHP/PDO. Цель SQL определяет, какие операторы и типы T-SQL поддерживаются, а ODBC осуществляет базовую коммуникацию. Подготовка один раз может сократить повторяющуюся работу, но не гарантирует конкретный план выполнения сервера или повышение производительности.

Шаблоны входной привязки применимы к SQL Server, База данных SQL Azure, Управляемый экземпляр SQL Azure, Azure Synapse Analytics, базе данных SQL в Microsoft Fabric и Хранилищу в Microsoft Fabric. Эти программы используют аутентификацию SQL для SQL Server. Для Azure onboarding предпочитайте аутентификацию Microsoft Entra. Оба продукта Fabric требуют этого, а Warehouse также требует отключения множественных активных наборов результатов (MARS). Проверьте поддержку выбранной цели на изменения схемы, транзакции и хранящиеся процедуры перед повторным использованием кода SQL Server. Для различий в конечных точках подключения и аутентификации см. Azure and Fabric connections.