結果データのフェッチ
ODBC アプリケーションでは、結果データのフェッチを 3 つの方法で実行できます。
1 つ目の方法は、SQLBindCol に基づきます。結果セットをフェッチする前に、SQLBindCol を使用して結果セット内の各列をプログラム変数にバインドします。列がバインドされると、アプリケーションで SQLFetch または SQLFetchScroll が呼び出されるたびに、ドライバは、結果セット列にバインドされた変数へ現在の列のデータを転送します。結果セット列のデータ型とプログラム変数のデータ型が異なる場合、ドライバによってデータ変換が処理されます。アプリケーションでは、SQL_ATTR_ROW_ARRAY_SIZE が 2 以上に設定されている場合、結果列を変数の配列にバインドできます。この配列では、SQLFetchScroll を呼び出すたびに値がすべて設定されます。
2 つ目の方法は、SQLGetData に基づきます。結果セット列をプログラム変数にバインドするのに SQLBindCol を使用しません。SQLFetch を呼び出すたびに、結果セット内の列ごとに SQLGetData を 1 回呼び出します。SQLGetData は、列と変数のデータ型を指定して、データを結果セットの特定の列から特定のプログラム変数に転送するようドライバに指示します。そのため、結果列のデータ型とプログラム変数のデータ型が異なる場合は、ドライバでデータを変換できます。通常、text 列、ntext 列、および image 列は大きすぎて 1 つのプログラム変数には格納できませんが、SQLGetData を使用して取得できます。結果列の text データ、ntext データ、または image データがプログラム変数よりも大きい場合、SQLGetData から SQL_SUCCESS_WITH_INFO と SQLSTATE 01004 (文字列データの右側が切り捨てられた状態) が返されます。SQLGetData を連続して呼び出すと、text データまたは image データの連続したチャンクが返されます。データの末尾に達すると、SQLGetData から SQL_SUCCESS が返されます。SQL_ATTR_ROW_ARRAY_SIZE に 1 よりも大きい値を指定すると、フェッチのたびに行セットが返されます。SQLGetData を呼び出す前に、まず SQLSetPos を使用して行セット内の特定の行を現在行に指定する必要があります。
3 つ目の方法では、SQLBindCol と SQLGetData を組み合わせて使用します。たとえば、アプリケーションでは、結果セットの最初の 10 列をバインドした後、毎回のフェッチ時に SQLGetData を 3 回呼び出して、3 つのバインドされていない列のデータを取得できます。一般に、このような方法は結果セットに 1 つ以上の text 列または image 列が含まれている場合に使用します。
結果セットのカーソル オプションによっては、アプリケーションで SQLFetchScroll のスクロール オプションを使用して、結果セットをスクロールすることもできます。
SQLBindCol を使用すると ODBC ドライバでメモリの割り当てが行われるので、結果セット列をプログラム変数にバインドするために SQLBindCol を数多く使用するとパフォーマンスが低下します。結果列を変数にバインドすると、SQLFreeHandle を呼び出してステートメント ハンドルを解放するか、fOption を SQL_UNBIND に設定して SQLFreeStmt を呼び出すまで、そのバインドは有効のままになります。バインドは、ステートメントが完了しても自動的には元に戻りません。
このロジックにより、異なるパラメータを指定して同じ SELECT ステートメントを何度も実行することで、効率を上げることができます。結果セットでは同じ構造が保持されるので、1 度結果セットをバインドすると、すべての SELECT ステートメントをそのバインドで処理し、最後のステートメントを実行した後で fOption を SQL_UNBIND に設定して SQLFreeStmt を呼び出すことができます。fOption を SQL_UNBIND に設定して SQLFreeStmt を呼び出し、それまでのバインドを解放しないで、さらに SQLBindCol を呼び出して結果セット内の列をバインドすることは避けてください。
SQLBindCol を使用すると、行方向または列方向のいずれのバインドも実行できます。行方向のバインドの方が、列方向のバインドよりも処理がやや高速になります。
SQLGetData を使用すると、SQLBindCol を使用して結果セット列をバインドするのではなく、列ごとにデータを取得できます。結果セットに数行しか行が含まれていない場合は、SQLBindCol ではなく SQLGetData を使用した方が処理は高速です。多数の行が含まれている場合は、SQLBindCol を使用する方が最適なパフォーマンスが得られます。異なる変数セットに対してデータを設定することがある場合は、毎回再バインドを行うのではなく、SQLGetData を使用してください。SQLGetData を選択リスト内の列に使用できるのは、すべての列を SQLBindCol を使用してバインドした場合のみです。また、選択リスト内の列は、SQLGetData で既に使用されているすべての列の後に指定する必要があります。
プログラム変数へのデータの移動またはプログラム変数からのデータの移動を処理する SQLGetData、SQLBindCol、SQLBindParameter などの ODBC 関数では、データ型の変換がサポートされます。たとえば、整数列を文字列プログラム変数にバインドするアプリケーションでは、プログラム変数にデータを格納する前に、ドライバが自動的にそのデータを整数から文字に変換します。
アプリケーションでのデータ変換は最小限に抑える必要があります。列やパラメータは、アプリケーションでの処理にデータ変換が必要な場合を除き、同じデータ型のプログラム変数にバインドする必要があります。ただし、あるデータ型から別のデータ型に変換する必要がある場合は、アプリケーションでのデータ変換よりもドライバによるデータ変換の方が効率的です。通常、SQL Server Native Client ODBC ドライバは、ネットワーク バッファからアプリケーションの変数にデータを直接転送します。データ変換を行うようにドライバに要求すると、ドライバはデータをバッファに格納し、CPU サイクルを使用してデータを変換します。
プログラム変数は、列から転送されたデータを保持するのに十分な大きさが必要です。ただし、text データ、ntext データ、image データは除きます。結果セットのデータを取得してそのデータを変数に格納するときに、その変数が小さすぎてデータを保持できない場合、ドライバは警告を生成します。その結果、ドライバでは警告メッセージ用のメモリを割り当て、ドライバとアプリケーションの両方で、警告メッセージの処理やエラーの処理に CPU サイクルが必要になります。アプリケーションでは、取得するデータを保持するのに十分なサイズの変数を割り当てるか、選択リストで SUBSTRING 関数を使用して結果セット内の列のサイズを小さくする必要があります。
SQL_C_DEFAULT を使用して C 変数のデータ型を指定する場合は注意が必要です。SQL_C_DEFAULT は、C 変数のデータ型と、列やパラメータの SQL データ型を一致させることを指定します。SQL_C_DEFAULT が ntext 列、nchar 列、または nvarchar 列に指定されると、Unicode データがアプリケーションに返されます。そのため、アプリケーションが Unicode データを処理するようにコーディングされていないと、さまざまな問題が発生する可能性があります。同様の問題が、uniqueidentifier (SQL_GUID) データ型についても発生する場合があります。
一般に、text データ、ntext データ、および image データは、大きすぎて 1 つのプログラム変数に格納できないので、通常、SQLBindCol ではなく SQLGetData を使用して処理します。サーバー カーソルを使用する場合、SQL Server Native Client ODBC ドライバは、バインドされていない text、ntext、または image 型の列のデータを行のフェッチ時に転送しないように最適化されています。アプリケーションから列に対して SQLGetData が実行されるまで、text、ntext、または image 型の列のデータはサーバーから取得されません。
ユーザーがカーソル内を単にスクロールしているときに、text データ、ntext データ、または image データを表示しないようにするために、この最適化をアプリケーションに適用できます。アプリケーションでは、ユーザーが行を選択してから、SQLGetData を呼び出して、text データ、ntext データ、または image データを取得することができます。そのため、ユーザーが選択していない行の text データ、ntext データ、または image データが転送されなくなり、その結果、膨大な量のデータ転送を抑えることができます。