ODBC テーブル値パラメーターの使用
適用対象: SQL Server Azure SQL Database Azure SQL Managed Instance Azure Synapse Analytics Analytics Platform System (PDW)
このトピックでは、ODBC でテーブル値パラメーターを使用する主なユーザー シナリオについて説明します。
複数行のバッファーに完全にバインドされるテーブル値パラメーター (すべての値をメモリ内に保持した TVP としてデータを送信する)
行のストリーミングを使用するテーブル値パラメーター (実行時のデータを使用する TVP としてデータを送信する)
システム カタログからテーブル値パラメーターのメタデータを取得
準備されたステートメント用にテーブル値パラメーターのメタデータを取得
複数行のバッファーに完全にバインドされるテーブル値パラメーター (すべての値をメモリ内に保持した TVP としてデータを送信する)
複数行のバッファーに完全にバインドされる形式で使用する場合、すべてのパラメーター値はメモリ内から使用できます。 このような形式は OLTP トランザクションなどでは一般的に行われ、テーブル値パラメーターを 1 つのストアド プロシージャにパッケージ化できます。 テーブル値パラメーターを使用しないと、複雑な複数のステートメントで構成されるバッチを動的に生成し、サーバーを複数回呼び出すことになります。
テーブル値パラメーター自体は、他のパラメーターと共に SQLBindParameter を使用してバインドされます。 すべてのパラメーターがバインドされると、アプリケーションは各テーブル値パラメーターにパラメーター フォーカス属性SQL_SOPT_SS_PARAM_FOCUSを設定し、テーブル値パラメーターの列に対して SQLBindParameter を呼び出します。
テーブル値パラメーターのサーバー型は、SQL_SS_TABLE新しい SQL Server 固有の型です。 SQL_SS_TABLE の C データ型へのバインドは、常に SQL_C_DEFAULT にする必要があります。 テーブル値パラメーターにバインドされたパラメーターについては、データが転送されません。このパラメーターは、テーブルのメタデータを渡し、テーブル値パラメーターを構成する列にデータを渡す方法を制御するために使用されます。
テーブル値パラメーターの長さには、サーバーに送信される行数が設定されます。 テーブル値パラメーターの SQLBindParameter の ColumnSize パラメーターは、送信できる行の最大数を指定します。これは列バッファーの配列サイズです。 ParameterValuePtr はパラメーター バッファーです。SQLBindParameter のテーブル値パラメーター、 ParameterValuePtr およびその関連する BufferLength は、必要に応じてテーブル値パラメーターの型名を渡すために使用されます。 型名は、ストアド プロシージャの呼び出しには必要ありませんが、SQL ステートメントには必要です。
SQLBindParameter の呼び出しでテーブル値パラメーターの型名を指定する場合、ANSI アプリケーションとしてビルドされているアプリケーションでも、常に Unicode 値として指定する必要があります。 SQLSetDescField を使用してテーブル値パラメーターの型名を指定する場合は、アプリケーションのビルド方法に準拠するリテラルを使用できます。 ODBC ドライバー マネージャーで、必要な Unicode 変換を実行します。
テーブル値パラメーターとテーブル値パラメーター列のメタデータは、SQLGetDescRec、SQLSetDescRec、SQLGetDescField、SQLSetDescField を使用して、個別および明示的に操作できます。 ただし、SQLBindParameter のオーバーロードは通常、より便利であり、ほとんどの場合、明示的な記述子アクセスは必要ありません。 この方法は、他のデータ型の SQLBindParameter の定義と一致します。ただし、テーブル値パラメーターの場合、影響を受ける記述子フィールドが若干異なる点が異なります。
アプリケーションでは、場合によっては、動的な SQL を含むテーブル値パラメーターを使用して、テーブル値パラメーターの型名を指定する必要があります。 この場合、テーブル値パラメーターが接続の現在の既定のスキーマで定義されていない場合は、SQLSetDescField を使用してSQL_CA_SS_SCHEMA_NAMEを設定する必要があります。 テーブル型定義とテーブル値パラメーターは同じデータベース内に存在する必要があるため、アプリケーションでテーブル値パラメーターを使用する場合は、SQL_CA_SS_CATALOG_NAMEを設定しないでください。 それ以外の場合、SQLSetDescField はエラーを報告します。
このシナリオのサンプル コードは、テーブル値パラメーター (ODBC) のdemo_fixed_TVP_binding
手順にあります。
行のストリーミングを使用するテーブル値パラメーター (実行時のデータを使用する TVP としてデータを送信する)
このシナリオでは、要求に応じてアプリケーションからドライバーに行が渡され、サーバーにストリーム送信されます。 これにより、すべての行をメモリ内にバッファリングする必要がなくなります。 これは、一括挿入や一括更新のシナリオの代表的な例です。 テーブル値パラメーターは、パフォーマンスの点ではパラメーター配列と一括コピーとの間に位置します。 つまり、テーブル値パラメーターは、パラメーター配列と同程度にプログラミングが容易ですが、サーバー側の柔軟性が増します。
テーブル値パラメーターとその列は、前の「複数行のバッファーに完全にバインドされるテーブル値パラメーター」で説明したとおりにバインドされますが、テーブル値パラメーター自体の長さのインジケーターは、SQL_DATA_AT_EXEC に設定されます。 ドライバーは、通常の方法で SQLExecute または SQLExecuteDirect に応答して、データ実行時パラメーター、つまり、SQL_NEED_DATAを返します。 ドライバーがテーブル値パラメーターのデータを受け入れる準備ができたら、SQLParamData は SQLBindParameter で ParameterValuePtr の値を返します。
アプリケーションでは、テーブル値パラメーターに SQLPutData を使用して、テーブル値パラメーター構成列のデータの可用性を示します。 テーブル値パラメーターに対して SQLPutData を呼び出す場合、 DataPtr は常に null で、 StrLen_or_Ind は 0 か、テーブル値パラメーター バッファー (SQLBindParameter の ColumnSize パラメーターに指定された配列サイズ以下である必要があります。 0 はテーブル値パラメーターの行がなくなったことを示すため、ドライバーはプロシージャの次の実パラメーターの処理に進みます。 StrLen_or_Indが 0 でない場合、ドライバーはテーブル値パラメーターの構成列を非テーブル値パラメーター バインド パラメーターと同じ方法で処理します。各テーブル値パラメーター列は、実際のデータ長、SQL_NULL_DATAを指定できます。また、実行時に長さ/インジケーター バッファーを使用してデータを指定することもできます。 テーブル値パラメーター列の値は、文字またはバイナリ値を一部で渡す場合に、通常どおり SQLPutData への繰り返し呼び出しによって渡すことができます。
テーブル値パラメーターのすべての列が処理されたら、ドライバーはテーブル値パラメーターに戻り、テーブル値パラメーターのデータの次の行を処理します。 したがって、実行時データのテーブル値パラメーターの場合、バインドされたパラメーターを順番にスキャンする通常の方法には従いません。 バインドされたテーブル値パラメーターは、SQLPutData が 0 に等しい StrLen_Or_IndPtr 呼び出されるまでポーリングされます。この時点で、ドライバーはテーブル値パラメーター列をスキップし、次の実際のストアド プロシージャ パラメーターに移動します。 SQLPutData が 1 以上のインジケーター値を渡すと、ドライバーは、バインドされたすべての行と列の値が含まれるまで、テーブル値パラメーターの列と行を順番に処理します。 その後、ドライバーはテーブル値パラメーターに戻ります。 SQLParamData からテーブル値パラメーターのトークンを受け取り、テーブル値パラメーターに対して SQLPutData(hstmt, NULL, n) を呼び出す間に、アプリケーションは、次の行または行をサーバーに渡すために、テーブル値パラメーター構成列データとインジケーター バッファーの内容を設定する必要があります。
このシナリオのサンプル コードは、Use Table-Valued Parameters (ODBC)のルーチン demo_variable_TVP_binding
にあります。
システム カタログからテーブル値パラメーターのメタデータを取得
アプリケーションがテーブル値パラメーターを持つプロシージャに対して SQLProcedureColumns を呼び出すと、DATA_TYPEはSQL_SS_TABLEとして返され、TYPE_NAMEはテーブル値パラメーターのテーブル型の名前になります。 SQLProcedureColumns によって返される結果セットに 2 つの列が追加されます。SS_TYPE_CATALOG_NAMEは、テーブル値パラメーターのテーブル型が定義されているカタログの名前を返し、SS_TYPE_SCHEMA_NAMEテーブル値パラメーターのテーブル型が定義されているスキーマの名前を返します。 ODBC 仕様に準拠して、SS_TYPE_CATALOG_NAMEとSS_TYPE_SCHEMA_NAMEは、以前のバージョンの SQL Server で追加されたすべてのドライバー固有の列の前、および ODBC 自体によって義務付けられたすべての列の後に表示されます。
新しい列は、テーブル値パラメーター用だけでなく、CLR ユーザー定義型パラメーター用にも作成されます。 UDT パラメーターの既存のスキーマ列とカタログ列も依然として作成されますが、共通のスキーマ列とカタログ列を必要とするデータ型にもそれらの列を含めておくと、今後アプリケーション開発が簡単になります (XML スキーマ コレクションは多少異なり、この変更には含まれていないことに注意してください)。
アプリケーションでは、SQLTable を使用して、永続テーブル、システム テーブル、およびビューの場合と同じ方法でテーブルの種類の名前を決定します。 アプリケーションでテーブル値パラメーターに関連付けられたテーブル型を識別できるように、新しいテーブル型として TABLE TYPE が導入されました。 テーブル型と通常のテーブルでは、異なる名前空間を使用します。 つまり、テーブル型と実際のテーブルに、同じ名前を使用できます。 これに対処するために、新しいステートメント属性として SQL_SOPT_SS_NAME_SCOPE が導入されました。 この属性は、テーブル名をパラメーターとして受け取る SQLTable やその他のカタログ関数が、テーブル名を実際のテーブルの名前またはテーブル型の名前として解釈するかどうかを指定します。
アプリケーションでは、SQLColumns を使用して、永続テーブルの場合と同じ方法でテーブル型の列を決定しますが、最初にSQL_SOPT_SS_NAME_SCOPEを設定して、実際のテーブルではなくテーブル型を操作していることを示す必要があります。 SQLPrimaryKeys は、SQL_SOPT_SS_NAME_SCOPEを使用して、テーブル型でも使用できます。
このシナリオのサンプル コードは、Use Table-Valued Parameters (ODBC)のルーチン demo_metadata_from_catalog_APIs
にあります。
準備されたステートメント用にテーブル値パラメーターのメタデータを取得
このシナリオでは、アプリケーションは SQLNumParameters と SQLDescribeParam を使用して、テーブル値パラメーターのメタデータを取得します。
IPD フィールド SQL_CA_SS_TYPE_NAME は、テーブル値パラメーターの型名を取得するために使用されます。 IPD フィールドSQL_CA_SS_SCHEMA_NAMEとSQL_CA_SS_CATALOG_NAMEは、それぞれカタログとスキーマを取得するために使用されます。
テーブル型の定義とテーブル値パラメーターは同じデータベースに存在する必要があります。 SQLSetDescField は、アプリケーションがテーブル値パラメーターを使用するときにSQL_CA_SS_CATALOG_NAMEを設定した場合にエラーを報告します。
SQL_CA_SS_CATALOG_NAMEとSQL_CA_SS_SCHEMA_NAMEを使用して、CLR ユーザー定義型パラメーターに関連付けられているカタログとスキーマを取得することもできます。 SQL_CA_SS_CATALOG_NAMEとSQL_CA_SS_SCHEMA_NAMEは、CLR UDT 型の既存の型固有のカタログ スキーマ属性の代替手段です。
SQLDescribeParam はテーブル値パラメーター列の列のメタデータを返さないため、アプリケーションでは SQLColumns を使用してテーブル値パラメーターの列メタデータも取得します。
このユース ケースのサンプル コードは、Use Table-Valued Parameters (ODBC)のルーチン demo_metadata_from_prepared_statement
にあります。