実行関数の比較

PHP ドライバーのダウンロード

両方のドライバーはステートメントを一度実行するか、繰り返し実行するためにステートメントを準備できます。 ユーザーが提供した値にはパラメータを使い、たとえクエリを一度しか実行しない場合でも使用してください。

実行APIは結果の取得方法とは別に選びましょう。 クエリを準備しても行は取得されず、結果をバッファリングしてもクエリがパラメータ化されるわけではありません。 以下の例では、読み取り専用のクエリを1回準備し、異なる値で2回実行し、次の実行前にそれぞれの結果を消費します。

実行スタイル SQLSRV PDO_SQLSRV
パラメーターなしでクエリを一度実行 sqlsrv_query PDO::query で結果を取得し、PDO::exec で影響を受けた行数を取得
パラメータ付きで一度クエリを実行してください sqlsrv_query($conn, $sql, $params) PDO::prepare() に続いて PDOStatement::execute()
一度準備して、異なるパラメータで繰り返し実行してください sqlsrv_prepare + sqlsrv_execute + PDOStatement::execute

sqlsrv_query() 1回の呼び出しで準備・実行し、文リソースを返します。 sqlsrv_prepare() 実行せずに文リソースを返します。各実行時に sqlsrv_execute() を呼び出してください。 PDOの query() は実行して PDOStatementを返しますが、 exec() は結果オブジェクトの代わりに影響を受けた行数を返します。 成功したexec()は0を返すことがあるため、失敗した戻り値は=== falseではなくif (!$count)と比較してください。

PDO::query() パラメータ値の配列を受け入れません。 オプションの引数はフェッチングの設定を行います。 パラメータ化されたPDO_SQLSRVクエリには prepare() と execute()を使用します。

一度準備すると、文の処理を異なる値で再利用できます。 これはサーバーが実行計画を再コンパイルしない保証ではありません。 PDO_SQLSRVはデフォルトで準備実行(prepared execution)を使用します。 PDO::query()も含めて; その直接クエリ属性 は、その選択を分離します。

一度準備して、繰り返し実行する

これらは別個の完全なコマンドラインプログラムです。 選択した拡張機能をインストールして読み込み、PHPプロセス環境で SQL_SERVER、 SQL_DATABASE、 SQL_USER、 SQL_PASSWORD を設定します。 SQL Serverテストデータベースとクライアントが信頼するサーバー証明書を使いましょう。 環境設定やパスワードレスの代替手段については、「 サーバーへの接続」を参照してください。

実際のデータベース名、ユーザー名、パスワードを環境変数に保存します。 両プログラムは、PHPドライバのODBCスタイルの接続処理で求められる通り、閉じ括弧を倍増させて各値を括弧で囲むことで、これらの値を一度だけ脱出します。 接続値のエスケープはクエリパラメータのバインディングとは別物です。

プログラムはテーブルを作成したりデータを変更したりせずに、2つのインライン行に対して Transact-SQL (T-SQL) クエリを実行します。 各プログラムをそれぞれの .php ファイルに保存し、 php <file>で実行します。 サンプルデータベースは不要です。

SQLSRV

&$productIdに推薦状$productId渡してください。 各 sqlsrv_execute() は現在の値を使います。 SQLSRV_FETCH_ASSOC 列名でキーされた配列を返します。 null結果はそれ以上行がないことを意味します。falseはエラーを示します。

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

bindParam() で一度だけ $productId バインドします。 各 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

配列内の値がPDOの execute() に渡されたものは PDO::PARAM_STRとして扱われます。 SQLが整数パラメータ(たとえば、パラメータ化されたTOPカウント)を必要とする場合は、PDO::PARAM_INTをbindValue()と共に呼び出し、その後、配列なしでexecute()を呼び出します。

クエリを変更しずに値をバインドする

パラメータマーカーは、テーブル名、列名、キーワード、ソート方向ではなく、値を表します。 ?の周りに引用符を置かないでください。 動的識別子または ASC/DESC には、信頼できないテキストを連結する代わりに、アプリケーション制御の許可リストから選択してください。 INリストの場合は、値ごとに1つのマーカーを用意してください。

Requirement SQLSRV PDO_SQLSRV
1回の実行で値をバインドする パラメータ配列を 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 必要に応じて、明示的なSQL型を指定して PHP null をバインドします。 PDO::PARAM_NULLにバインドします。
出力パラメーターを受信 参照変数と一緒に SQLSRV_PARAM_OUT または SQLSRV_PARAM_INOUT を使用します。 PDO::PARAM_INPUT_OUTPUTと必要な種類・長さのbindParam()を使いましょう。

プリペアドステートメントの実行中は、参照される変数を有効なまま保持してください。 SQLSRVプログラムはパラメータ配列を置き換えるのではなく、バウンド変数を変更します。 PDOプログラムはバインディング後に配列なしで意図的に execute() を呼び出します。そのパターンを配列ベースの実行と混同しないでください。

ストアドプロシージャからの出力値は、プロシージャの結果セットの処理完了を含め、独自の処理が必要です。 これらの入力のみの例から出力パラメータの挙動を推測しないでください。 詳細については、「 方向性パラメータの使用」を参照してください。

実行およびフェッチ動作の設定

設定 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のプリペア、実行、フェッチ操作を別々に確認してください。 prepare が成功しても、実行が成功するとは限りません。サーバーはパラメータ値、権限、またはステートメントを実行時に拒否することがあります。 作戦が戻ったらすぐにsqlsrv_errors()false捕獲。 SQLSRVは多くの警告をデフォルトでエラーとして扱うため、コレクション全体を検査してください。

PDOの場合、例外モードでの接続障害やデータベースエラーが PDOExceptionを投げかけます。 保護されたアプリケーション診断でSQLSTATEおよびドライバーエラー情報を検査し、HTTP応答で例外テキストや認証情報を返さないでください。 prepare() 成功したことは、そのステートメントがすでに実行された証拠ではありません。

症状: アクション
すべての実行は最初の値を使います SQLSRVの場合は、参照で変数をバインドします。 PDOの場合は、変化する変数には bindParam() を使ったり、新しい値ごとに bindValue() を呼び出します。
TOPカウントのような数値パラメータは却下されます 必要な整数型を明示的にバインドします。 PDO execute([$value]) は、PHPの値が整数であっても文字列パラメータを使用します。
2つ目のコマンドは、未定の結果を報告します 結果を消費するか解放してから、接続を再利用してください。 PDOの場合は、残りの結果が不要なときに使う closeCursor() を使いましょう。
期待出力パラメータは変わっていません すべてのプロシージャ結果を入力し、パラメータの方向、PHP型、出力バッファ長を検証します。
メモリ使用量は結果サイズに比例して増加します fetchAll()または不要なクライアントバッファリングをシーケンシャルフェッチに置き換えましょう。

例は発言を解放し、 finallyでそのつながりを解放しています。 彼らは取引を始めません。 複数の書き込みが同時にコミットするか、すべてロールバックしなければならない場合は明示 的なトランザクション を用いましょう。 プリペアドステートメントを再利用しても書き込みはトランザクションにまとめられません。

ターゲットと実装の違い

prepare/execute APIとPHP参照ルールは拡張機能とPHP/PDOに属します。 SQLターゲットはどのT-SQL文や型をサポートするかを決定し、ODBCが基盤となる通信を行います。 一度準備すれば繰り返し作業を減らすことはできますが、特定のサーバー実行計画や速度向上を保証するわけではありません。

入力バインディングパターンはSQL Server、Azure SQL Database、Azure SQL Managed Instance、Azure Synapse Analytics、Microsoft FabricのSQL Database、Microsoft FabricのWarehouseに適用されます。 これらのプログラムはSQL Serverに対してSQL認証を使用します。 Azure onboardingの場合は、Microsoft Entra認証を優先してください。 Fabric製品の両方がこれを要求しており、Warehouseも複数のアクティブ結果セット(MARS)を無効化する必要があります。 SQL Serverコードを再利用する前に、選択したターゲットのスキーマ変更、トランザクション、ストアドプロシージャのサポートを確認してください。 エンドポイントと認証の違いについては、AzureおよびFabric接続を参照してください。