この記事では、SQL Server、Azure SQL Database、Azure SQL Managed Instance、Azure Synapse Analytics、およびMicrosoft FabricのSQLデータベースに対して高速PHPコードを書く方法を解説しています。 このガイダンスは、同じ基盤Microsoft ODBC Driver for SQL ServerをラップしているSQLSRVとPDO_SQLSRVの両方に適用されます。
最も影響の大きい変更から始めましょう
変更が3つだけできる場合は、以下の通りに変更してください:
- 接続プーリングを有効にしてください。 SQL Serverへの新しいTLS接続の確立には、ネットワークパスやTLS交渉によって数十から数百ミリ秒かかります。 プールされた接続を再利用することで、リクエストごとのコストを削減できます。 「 つながりを効率的に管理する」を参照してください。
- 必要な列と行だけを取得してください。
SELECT *そして、無制限クエリは遅いエンドポイントの最も一般的な原因です。 必要なものだけをクエリにしてください。 - 大量挿入にはテーブル値のパラメータを使用してください。 数百行以上の場合、テーブル値パラメータ(TVP)は通常、行ごとの
INSERT文よりもはるかに高速であり、行数に応じて線形にスケールします。 「 効率的にデータを挿入」を参照してください。
つながりを効率的に管理する
接続確立は、ドライバーが行う最もコストの高い操作です。 ほとんどのPHPパフォーマンス調査は、接続管理の修正で終わります。
接続プールを有効にする
プーリングは、リクエスト終了時にODBC接続を切断するのではなく、PHPリクエスト間でODBC接続を再利用します。 スクリプト終了時に接続オブジェクトは破棄されますが、基盤となるODBCハンドルはODBCドライバーマネージャーのプール内で存続し、同じ接続文字列を求める次のリクエストで再利用されます。
Windows:接続プーリングはデフォルトでオンになっています。 確認のために、DSNから ConnectionPooling オプションを外してください。 デバッグ用のプーリングを無効にするには、 ConnectionPooling=0を設定します。
LinuxとmacOS:これらのプラットフォームでは接続プーリングはDSNオプションではありません。 ドライバーマネージャーのodbcinst.iniの[ODBC]セクションでPooling=Yesを設定し、ドライバーのスタンザの下にポジティブなCPTimeoutを設定してください。 例えば次が挙げられます。
[ODBC]
Pooling=Yes
[ODBC Driver 18 for SQL Server]
Description=Microsoft ODBC Driver 18 for SQL Server
Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.<version>.so.1.1
CPTimeout=120
実際の図書館の道筋は odbcinst -q -d -n "ODBC Driver 18 for SQL Server" や ls /opt/microsoft/msodbcsql18/lib64/で見つけてください。 ファイル名はインストールされたODBCドライバーのバージョンを埋め込み、リリースごとに変更されます。
CPTimeout (数秒単位で)アイドル接続がプールにどれだけ長く留まるかを制御し、閉じるまで制御します。 ほとんどのリクエストがプール接続に到達できるくらい高く設定しつつ、フェイルオーバーしたサーバーへの古い接続が比較的早く終了できるくらい低く設定してください。 ほとんどのウェブ作業では60秒から300秒で十分です。
詳細は 「接続プーリング」を参照してください。
初回問い合わせコストを理解する
複数のアクティブな結果セット (MARS) は既定では有効になっています。 MARSと接続プーリングの両方が有効な場合、 ドライバーは最初の クエリでプールされた接続をリセットし、そのリセットは最初のクエリで設定したクエリタイムアウトを無視します。 同じ接続での後からのクエリは通常タイムアウトを尊重します。 プールされたワークロードに対して攻撃的な初回クエリタイムアウトを設定する場合は、この挙動を考慮し、不要ならMARSを無効にするか MultipleActiveResultSets=false してください。 MARSおよびプーリングの注記は 「Connection Pooling」を参照してください。
永続的なPDO接続はサポートされていません
PDO_SQLSRV PDO::ATTR_PERSISTENTを拒否する。 構成体投げに設定すると:
SQLSTATE[IMSSP]: An unsupported attribute was designated on the PDO object.
クロスリクエスト再利用には ODBC接続プーリング を使用してください。 これはドライバーネイティブの仕組みで、PDO_SQLSRVとSQLSRVの両方で動作し、CPTimeoutのアイドル接続を廃止します(これによりトークンリフレッシュも正当に保Microsoft Entraます)。
リクエスト内で接続を再利用します
プーリングがあっても、新しいPDOやSQLSRV接続を開くと、プーリングされたハンドルの取得と検証のためにODBCの往復が発生します。 リクエストごとに1回だけ接続を開き、必要なすべての関数に渡します。
Tip
依存性注入コンテナやレイジーアクセサで十分です。 重要なのは、リクエストハンドラの途中で new PDO(...) を避けることです。
必要なものだけをクエリしてください
ネットワークの往復や結果セットのマテライズがほとんどのPHPワークロードのクエリ遅延を支配しています。 修正方法はすべてのデータベースアクセス層に適用されるものです。
使用する列だけを選択してください
SELECT * 実際に消費するデータよりもはるかに小さい varchar(max) や varbinary(max) の列を含め、すべての列を取得します。 列の名前を挙げてください:
<?php
// Slow: fetches all columns, including a 2 MB LOB column
$stmt = $conn->query("SELECT * FROM dbo.Products");
// Fast: fetches only the two columns the caller uses
$stmt = $conn->query("SELECT ProductID, Name FROM dbo.Products");
必要な行だけ取ってきてください
SQL Serverにフィルタリングをプッシュします。
foreachループでフィルタリングするためだけにPHPにフルテーブルを取ってくるのは絶対に避けてください。
<?php
// Slow: transfers every row to PHP, then filters
$rows = $conn->query("SELECT * FROM dbo.Orders")->fetchAll(PDO::FETCH_ASSOC);
$recent = array_filter($rows, fn($r) => $r["OrderDate"] > "2026-01-01");
// Fast: filters on the server
$stmt = $conn->prepare("SELECT OrderID, CustomerID, Total FROM dbo.Orders WHERE OrderDate > ?");
$stmt->execute(["2026-01-01"]);
$recent = $stmt->fetchAll(PDO::FETCH_ASSOC);
大きな結果セットの改ページ
数百万行のうち数百行だけが表示されるリストビューの場合、すべての行を返さず、クライアントに整理させてください。サーバー側ページを OFFSET ... FETCHで使用してください:
<?php
function fetchPage(PDO $conn, int $page, int $pageSize): array {
$stmt = $conn->prepare(
"SELECT OrderID, CustomerID, Total
FROM dbo.Orders
ORDER BY OrderID
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"
);
// With native prepares, execute([...]) binds values as strings.
// OFFSET and FETCH NEXT require integer bindings; bind explicitly.
$stmt->bindValue(1, ($page - 1) * $pageSize, PDO::PARAM_INT);
$stmt->bindValue(2, $pageSize, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
適切なフェッチ方法を選ぶ
- すべての行を一度にメモリに持たせないときは、ストリーミングの反復に
fetch(PDO::FETCH_ASSOC)をループで使うのが良いでしょう。 - 呼び出し元が本当に全セットを必要とする場合(例えば、完全なJSONレスポンスをレンダリングするなど)に
fetchAll(PDO::FETCH_ASSOC)を使います。 -
fetchColumn()は、COUNT、SUM、MAXのスカラーだけを気にするときに使います。 -
PDO::FETCH_KEY_PAIRやPDO::FETCH_UNIQUEを使って、二度目の手を加えずに検索辞書を作成できます。
数値フェッチモード(PDO::FETCH_NUM)は、列名マップの構築を省略するため、結合フェッチモードよりわずかに高速です。 明快さを優先し、プロファイラーがフェッチオーバーヘッドを重要と判断した場合にのみ切り替えてください。
ストアドプロシージャやバッチで Preferred in StolySET NOCOUNT ON
すべての INSERT、 UPDATE、 DELETE 文は影響を受けた行数を含む DONE_IN_PROC トークンを返しますが、PHPは通常これを破棄します。 トークンは往復のルートを追加しませんが、各トークンにはワイヤー上のバイトと少量のドライバー作業がかかります。 1回の呼び出しで何百もの文を実行するマルチステートメントプロシージャやバッチでは、その節約効果が積み重なります。 電源を切る:
CREATE OR ALTER PROCEDURE dbo.ProcessOrder
@OrderID INT
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Inventory SET Stock = Stock - 1 WHERE ProductID IN (SELECT ProductID FROM dbo.OrderLines WHERE OrderID = @OrderID);
UPDATE dbo.Orders SET Status = 'Processed' WHERE OrderID = @OrderID;
END;
効率的にデータを挿入する
移動する行数に応じて適切な挿入方法を選びましょう。 間違った選択は100倍遅くなることがあります。
約100行未満:ループ内の準備文
小規模バッチの場合は、ループ内で1つの準備済み文を実行します:
<?php
$stmt = $conn->prepare("INSERT INTO dbo.Products (Name, Price) VALUES (?, ?)");
foreach ($products as $p) {
$stmt->execute([$p["name"], $p["price"]]);
}
トランザクションでループをラップして、すべてのインサートが一つのユニットとしてコミットされ、ログが毎行フラッシュされる必要がなくなります:
<?php
$conn->beginTransaction();
try {
$stmt = $conn->prepare("INSERT INTO dbo.Products (Name, Price) VALUES (?, ?)");
foreach ($products as $p) {
$stmt->execute([$p["name"], $p["price"]]);
}
$conn->commit();
} catch (PDOException $e) {
$conn->rollBack();
throw $e;
}
数百行から数百万行:テーブル値パラメータ
テーブル値パラメータ(TVP)はバッチ全体を一度の往復でSQL Serverに送り、SQL Serverがその集合を単一の文として処理できるようにします。 数百行以上のバッチの場合、TVPは通常、準備文ループよりもはるかに高速であり、行数に応じて線形にスケールします。
まず、サーバー上でテーブル型を作成します:
CREATE TYPE dbo.ProductTableType AS TABLE (
Name NVARCHAR(100),
Price DECIMAL(10, 2)
);
PDO_SQLSRV TVPを型名、値が行集合の鍵を持つ連想配列として渡します。
PDO::PARAM_LOBで綴じてください:
<?php
$rows = [];
foreach ($products as $p) {
$rows[] = [$p["name"], $p["price"]];
}
$tvpInput = ["ProductTableType" => $rows];
$stmt = $conn->prepare(
"INSERT INTO dbo.Products (Name, Price) SELECT Name, Price FROM ?"
);
$stmt->bindParam(1, $tvpInput, PDO::PARAM_LOB);
$stmt->execute();
デフォルトでないスキーマの場合は、そのスキーマを配列の次の要素として渡します: ["ProductTableType" => $rows, "Sales"]。 SQLSRVの手続き型構文およびストアドプロシージャの例については、「 テーブル値パラメータの使用」を参照してください。
数百万行:bcp または BULK INSERT
本当に大量作業(データウェアハウスの読み込みや初期移行)には、PHPの代わりに bcp や BULK INSERT を使いましょう。 データを区切られたファイルやネイティブフォーマットのファイルに書き込み、その後、スケジュールされたジョブ、ETLステップ、または管理スクリプトからbcpや BULK INSERT を実行します。
Caution
PHPから shell_exec() や proc_open()を使ってBCPに支払う場合は、信頼できない入力をコマンドラインに補間しないでください。 すべての引数に escapeshellarg() を使い、ウェブリクエストパスではなくアウトオブバンドでロードを実行することを好む。
往復の回数を減らす
PHPとSQL Server間のすべてのネットワーク往復には固定のコストがかかります。 5つの明細を一括で送ると、そのコストを5回ではなく1回支払うことになります。
関連する文を一つのバッチにまとめる
関連する作業が同時に動作する場合は、文を一つのバッチにまとめてすべての結果セットを消費します。
<?php
$sql = "
SELECT * FROM dbo.Customers WHERE CustomerID = ?;
SELECT * FROM dbo.Orders WHERE CustomerID = ?;
SELECT * FROM dbo.Addresses WHERE CustomerID = ?;
";
$stmt = $conn->prepare($sql);
$stmt->execute([$id, $id, $id]);
$customer = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->nextRowset();
$orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->nextRowset();
$addresses = $stmt->fetchAll(PDO::FETCH_ASSOC);
SQLSRVの場合は、結果セット間の進みに sqlsrv_next_result を使いましょう。
必要に応じて複数のアクティブ結果セットを有効にする
マルチプルアクティブ結果セット(MARS)は、単一の接続に複数のアクティブステートメントを持つことを可能にします。 MARSがなければ、まだ結果セットが開いている接続に対して新しいクエリを発行することはできません。 両方のドライバーはデフォルトでMARSを有効にしています。 停止するには、接続文字列にMultipleActiveResultSets=falseを設定してください。 「 複数のアクティブ結果セット(MARS)を無効にする」を参照してください。
MARSは便利ですが無料ではありません。 各アクティブな結果セットはサーバーサイドのリソースを消費します。 結果セットを完全に消化してから次のセットを始めることを好みます。 MARSを使って、本当にネストされたカーソルパターンをブロック解除しましょう。
用意された声明をチューニングしてください
準備済み文は、サーバー上でSQLを再解析するドライバーを省き、信頼できない入力を安全にパラメータとしてバインドできるようにします。
地元の調理法を好みます
PDO_SQLSRVは2つのモードでステートメントを作成できます。
ネイティブPreparesは SQLテキストを一度サーバーに送信し、解析した文を各実行で再利用し、各 execute()のパラメータ値のみを送信します。
エミュレート準備は クライアントにSQLテキストを保持し、各実行時にパラメータ補間した完全なSQL文字列を再構築します。
ドライバーがネイティブの準備を使うように PDO::ATTR_EMULATE_PREPARES => false 設定してください。 ネイティブPreparesはSQL Serverがクエリプランをキャッシュして再利用でき、実行ごとにSQLテキストを再解析する必要を避けられます。
<?php
$conn = new PDO($dsn, null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
用意された声明の再利用
一度準備して、たくさん処刑する。 各 prepare() 通話にはODBCハンドル割り当てとサーバー側の解析が必要です。 ホットループでは、 $stmt オブジェクトを生かし続け、ループ内の execute() を呼び出します:
<?php
// Fast: one prepare, many executes.
$stmt = $conn->prepare("UPDATE dbo.Inventory SET Stock = Stock - ? WHERE ProductID = ?");
foreach ($orderLines as $line) {
$stmt->execute([$line["qty"], $line["productId"]]);
}
// Slow: re-prepares the same SQL on every iteration.
foreach ($orderLines as $line) {
$stmt = $conn->prepare("UPDATE dbo.Inventory SET Stock = Stock - ? WHERE ProductID = ?");
$stmt->execute([$line["qty"], $line["productId"]]);
}
TOP (?)に気をつけてIN (?, ?, ...)
TOPパラメータマーカーSELECT TOP (?) ...の周りに括弧が必要なため、行数をパラメータとして解析できるSQL Server。
IN (?, ?, ?, ?) 準備時に固定のプレースホルダー数が必要です。 動的 IN リストサイズの場合、検証済みの整数カウントからプレースホルダー文字列を作成するか、テーブル値パラメータとしてリストを渡します。
Caution
生のユーザー入力をSQLテキスト(プレースホルダー数を含む)に補間してはいけません。 プレースホルダー文字列を作成する前に (int) でカウントをキャストし、実際の値をパラメータとして execute() に必ず通します。
カーソルとメモリの管理
デフォルトのカーソルタイプは PDO::CURSOR_FWDONLYで、前方専用のファイアホースです。 行を1行ずつPHPにストリーミングしバッファを持たないため、大きな結果セットは行数ではなく行バッファメモリで制限されます。 それがたいてい望むものです。
後ろに移動したり行数を数える必要がある時だけバッファーカーソルを使ってください
PDO::SQLSRV_CURSOR_BUFFERED (クライアント側バッファ付き静的カーソル)は、結果セット全体を最初にPHPメモリに取得します。 この方法なら rowCount()を呼び出し、逆方向に求め、文を再利用できます。 デフォルトでは、 PDO::SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZEを通じてバッファは10,240 KB(10 MB)に上限が設けられており、上限を超えるクエリはPHPメモリのオーバーフローではなく、 false を返します。 上限をPHPメモリ制限に引き上げることはできますが、その場合はクエリが新しい上限を超えた際に、 false リターンを犠牲にしてしまい、 Allowed memory size exhausted 致命的なエラーが発生します。 意図的にチューニングしてください。 カー ソルの種類(PDO_SQLSRV)を参照してください。
サーバー側のスクロール可能なカーソル(PDO::SQLSRV_CURSOR_STATIC、 PDO::SQLSRV_CURSOR_DYNAMIC、 PDO::SQLSRV_CURSOR_KEYSET)はクライアントではなくサーバー側にバッファされるため、PHPメモリを消費しません。 しかし、カーソルの間ずっとサーバー側のリソースを保持し、フォワードオンリーよりも1行あたりの処理速度が遅いです。
ストリーミングリードにはデフォルトの転送専用を使いましょう。
rowCount()や後ろスクロールが必要なときは、クライアント側でバッファリングされた小さな結果セットを使いましょう。 特定のことをしない限り、サーバー側のスクロール可能なカーソルは避けてください。
<?php
// Fast, low memory: default forward-only, one row at a time
$stmt = $conn->prepare("SELECT OrderID, Total FROM dbo.Orders");
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// ...
}
// Buffered: only when you need rowCount() or seeking
$stmt = $conn->prepare("SELECT * FROM dbo.SmallLookup", [
PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL,
PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED,
]);
$stmt->execute();
$rowCount = $stmt->rowCount();
詳細な説明については、 カーソルタイプ(PDO_SQLSRV) および カーソルタイプ(SQLSRV)を参照してください。
大きなバイナリおよび文字値のストリーム
varbinary(max)、varchar(max)、nvarchar(max)、xml、その他の大きな型では、メモリに値全体を物質化する代わりにPHPストリームを使用します。
<?php
$stmt = $conn->prepare("SELECT Name, PhotoBlob FROM dbo.Products WHERE ProductID = ?");
$stmt->execute([$id]);
$stmt->bindColumn("PhotoBlob", $photo, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);
// $photo is a stream resource; write it directly to disk without loading it all
$outFile = fopen("/tmp/photo.bin", "wb");
stream_copy_to_stream($photo, $outFile);
fclose($outFile);
大きな値を挿入または更新する場合は、SQLSRVの SendStreamParamsAtExec=false を使って sqlsrv_execute()後にストリームデータをチャンク単位で送信します。 詳細は「 ストリームとしてデータを送信」を参照してください。
適切なタイムアウトを設定する
タイムアウトは信頼性設定であると同時にパフォーマンス設定でもあります。 長く吊るすクエリはプール接続を保持し、他のリクエストを飢えさせてしまいます。
ステートメントのタイムアウト
ランアウェイクエリがプール接続を無期限に保持しないように、文ごとの時間制限を設定しましょう。 PDO_SQLSRVについて:
<?php
$stmt = $conn->prepare("SELECT ... FROM dbo.HugeTable ...");
$stmt->setAttribute(PDO::SQLSRV_ATTR_QUERY_TIMEOUT, 30); // seconds
$stmt->execute();
SQLSRVの場合は、options配列で "QueryTimeout" => 30 を sqlsrv_query か sqlsrv_prepareに渡します。
自分の作業量に合った値を設定してください。 同期ウェブリクエストの場合、通常15秒から30秒です。 バックグラウンドのバッチ作業なら、数分が妥当かもしれません。 ウェブリクエストでタイムアウトをゼロ(無制限)に設定しないでください。
ログイン タイムアウト
LoginTimeout接続文字列では、ドライバーが接続を確立するまでの待ち時間を制御します。 Azure SQL DatabaseやAzure SQL Managed Instanceに接続する際に明示的な値を設定し、コールドスタートやフェイルオーバーグループのフェイルオーバーがクライアントを無期限にハングさせないようにしましょう。 30秒から90秒の値はほとんどのクラウドワークロードに適しています。
ConnectRetryCount * ConnectRetryIntervalに対するLoginTimeoutのサイズ化およびその結果としての故障モードの詳細については、接続タイムアウトを参照してください。 オプションの参照については「 接続オプション」を参照してください。
読み取り専用ワークロードをレプリカにルーティングする
Always Onの可用性グループ、Azure SQL Managed Instance、またはリードスケールアウトやジオレプリカを持つAzure SQL Databaseのデータベースに対して読み取り専用クエリを行う場合、接続文字列に追加ApplicationIntent=ReadOnly:
<?php
$dsn = "sqlsrv:Server=<listener>;Database=<database>;" .
"Encrypt=true;ApplicationIntent=ReadOnly";
読み取り専用ルーティングは、接続を同期されたセカンダリカに送信し、プライマリからの作業をオフロードします。
MultiSubnetFailover=trueと組み合わせて、マルチサブネットの可用性グループリスナーへの最速接続を行ってください。
サーバーのパフォーマンスを観察してください
クライアント側のタイミングは、クエリがエンドツーエンドにどれくらいかかった時間を示すだけです。 なぜ遅いのかを調べるには、SQL Serverの内蔵診断機能を使ってください。
クエリ ストア
クエリ ストアは、データベース上のすべてのクエリに対して実行計画、ランタイム統計、待機統計をキャプチャします。 Azure SQL Database、Azure SQL Managed Instance、FabricのSQL Databaseではデフォルトで有効化されています。 SQL Serverでは、データベースごとに有効にしてください:
ALTER DATABASE <database_name> SET QUERY_STORE = ON;
次に、SQL Server Management Studioのクエリ ストアレポートを使って、最も遅く、最も頻繁に実行されるクエリを見つけてください。 クエリ ストアでのパフォーマンス監視を参照してください。
Azure SQL Query Performance Insight
Azure SQL Databaseの場合、Azureポータルのクエリパフォーマンスインサイトは設定なしで自動的にリソースを最も多く消費するクエリを表示します。 詳細については、「Azure SQL Database の Query Performance Insight」を参照してください。
SET STATISTICS 一度きりの調査のために
プロファイルしたい単一のクエリについては、統計をオンにしてSQL Server Management Studioで実行してください:
SET STATISTICS TIME ON;
SET STATISTICS IO ON;
-- your query here
高い論理リードはほとんどの場合、インデックスが欠落しているか使い物にならないことを意味します。 高いCPU時間で論理読み込みが少ない場合、通常は悪い計画(パラメータスニッフィング、インデックス使用を妨げる暗黙の変換、並列処理を妨げるスカラー関数)を意味します。
ドライバーレベルの追跡のための拡張イベント
ドライバーがSQL Serverに何を送信するか(補間する実際のパラメータ値を含む)を正確に確認するには、rpc_completedとsql_batch_completedイベントを使って拡張イベントセッションをキャプチャします。
パフォーマンス チェックリスト
このチェックリストを、SQL Serverに接続するPHPアプリケーションの事前レビューとして活用してください:
| 分野 | 確認 | Reference |
|---|---|---|
| Connection | プラットフォーム向けに接続プーリングが有効化され、設定されています | つながりを効率的に管理する |
| Connection | アプリケーションはリクエスト内の接続を再利用し、クエリごとに接続を開きません | リクエスト内で接続を再利用します |
| Connection |
LoginTimeoutAzure SQLのコールドスタートとフェイルオーバーをカバーしています |
ログインタイムアウト |
| Query | クエリは必要な列だけを選択します。 SELECT * |
使用する列だけを選択してください |
| Query | フィルタリングはSQLで行われ、PHPでは行われません。 array_filter |
必要な行だけ取ってきてください |
| Query | 大きな結果セットは、 OFFSET ... FETCH |
大きな結果集合をページ化 |
| Query | ストアドプロシージャセット SET NOCOUNT ON |
好み SET NOCOUNT ON |
| 挿入 | バルクインサートはテーブル値のパラメータを使用し、行ごとのループではありません | 効率的にデータを挿入する |
| Statements |
PDO::ATTR_EMULATE_PREPARES が false に設定されている |
地元の調理法を好みます |
| Statements | アプリケーションは実行間で準備済み文を再利用します | 用意された声明の再利用 |
| Cursors | バッファリングが必要な場合を除き、アプリケーションのデフォルトのフォワードオンリーカーソルを使用します | カーソルとメモリの管理 |
| 記憶 | 大きなバイナリ値や文字値はストリーミングされるもので、物質化されません | 大きなバイナリおよび文字値のストリーム |
| タイムアウト | 文のタイムアウトは、すべてのユーザー向けクエリに対して設定されます | ステートメントタイムアウト |
| 経路選択 | レプリカが存在する場所 ApplicationIntent=ReadOnly 設定する読み取り専用ワークロード |
読み取り専用ワークロードのルーティング |
| 可観測性 | クエリ ストアは有効化され、定期的にレビューされています | クエリ ストア |