注
次のテクニカル ノートは、最初にオンライン ドキュメントに含まれてから更新されていません。 その結果、一部の手順やトピックが古くなっているか、正しくない可能性があります。 最新情報については、オンライン ドキュメント インデックスで関心のあるトピックを検索することをお勧めします。
このノートでは、MFC データベース クラスを使用して ODBC SQL_LONGVARCHAR および SQL_LONGVARBINARY データ型を取得して送信する方法について説明します。
Long Varchar/Varbinary サポートの概要
ODBC SQL_LONG_VARCHAR と SQL_LONGBINARY データ型 (ここでは長いデータ列と呼ばれます) は、膨大な量のデータを保持できます。 このデータを処理する方法は 3 つあります。
CString
/CByteArray
にバインドします。CLongBinary
にバインドします。データベース クラスに関係なく、それをバインドして長いデータ値を手動で取得して送信しないでください。
3 つの方法にはそれぞれ長所と短所があります。
長いデータ列は、クエリのパラメーターではサポートされていません。 これらは outputColumns でのみサポートされています。
長いデータ列を CString/CByteArray にバインドする
利点:
この方法は簡単に理解でき、使い慣れたクラスを使用します。 フレームワークでは、DDX_Text
を使用したCString
のCFormView
サポートが提供されます。
CString
クラスとCByteArray
クラスを使用する一般的な文字列またはコレクション機能が多数あり、データ値を保持するためにローカルに割り当てられるメモリの量を制御できます。 フレームワークは、 Edit
または AddNew
関数呼び出し中にフィールド データの古いコピーを保持し、フレームワークは自動的にデータの変更を検出できます。
注
CString
は文字データの操作用に設計されており、バイナリ データを操作するためのCByteArray
であるため、文字データ (SQL_LONGVARCHAR) をCString
に、バイナリ データ (SQL_LONGVARBINARY) をCByteArray
に配置することをお勧めします。
CString
およびCByteArray
の RFX 関数には、データ列の取得値を保持するために割り当てられたメモリの既定のサイズをオーバーライドできる追加の引数があります。 次の関数宣言の nMaxLength 引数に注意してください。
void AFXAPI RFX_Text(CFieldExchange* pFX,
const char *szName,
CString& value,
int nMaxLength = 255,
int nColumnType =
SQL_VARCHAR);
void AFXAPI RFX_Binary(CFieldExchange* pFX,
const char *szName,
CByteArray& value,
int nMaxLength = 255);
長いデータ列を CString
または CByteArray
に取得する場合、返される最大データ量は既定で 255 バイトです。 それ以外の値は無視されます。 この場合、フレームワークは例外 AFX_SQL_ERROR_DATA_TRUNCATEDをスローします。 幸い、nMaxLength を MAXINT まで、より大きな値に明示的に増やすことができます。
注
nMaxLength の値は、 SQLBindColumn
関数のローカル バッファーを設定するために MFC によって使用されます。 これはデータの格納用のローカル バッファーであり、ODBC ドライバーによって返されるデータの量には実際には影響しません。
RFX_Text
RFX_Binary
バックエンド データベースからデータを取得するSQLFetch
を使用して呼び出しを 1 つだけ行います。 各 ODBC ドライバーには、1 回のフェッチで返すことができるデータの量に関して異なる制限があります。 この制限は、nMaxLength に設定された値よりもはるかに小さい場合があります。この場合、例外 AFX_SQL_ERROR_DATA_TRUNCATED がスローされます。 このような状況では、すべてのデータを取得できるように、RFX_Text
またはRFX_Binary
の代わりにRFX_LongBinary
を使用するように切り替えます。
ClassWizard は 、SQL_LONGVARCHAR を CString
にバインドするか、 SQL_LONGVARBINARY を CByteArray
にバインドします。 長いデータ列を取得する 255 バイトを超えるバイトを割り当てる場合は、nMaxLength に明示的な値を指定できます。
長いデータ列が CString
または CByteArray
にバインドされている場合、フィールドの更新は、SQL_VARCHAR または SQL_VARBINARY にバインドされている場合と同じように機能します。
Edit
中、データ値の変更を検出し、列のダーティ値と Null 値を適切に設定するためにUpdate
が呼び出されたときに、データ値がキャッシュされ、後で比較されます。
長いデータ列を CLongBinary にバインドする
長いデータ列に含まれるデータの MAXINT バイト数が多い場合は、 CLongBinary
に取得することを検討する必要があります。
利点:
これにより、使用可能なメモリまで、長いデータ列全体が取得されます。
短所:
データはメモリに保持されます。 この方法は、非常に大量のデータに対しても非常にコストがかかります。 バインドされたデータ メンバーの SetFieldDirty
を呼び出して、フィールドが Update
操作に含まれていることを確認する必要があります。
長いデータ列を CLongBinary
に取得する場合、データベース クラスは長いデータ列の合計サイズを確認し、データ値全体を保持するのに十分な大きさの HGLOBAL
メモリ セグメントを割り当てます。 その後、データベース クラスは、割り当てられた HGLOBAL
にデータ値全体を取得します。
データ ソースが長いデータ列の予想サイズを返すことができない場合、フレームワークは例外 AFX_SQL_ERROR_SQL_NO_TOTALをスローします。
HGLOBAL
を割り当てようとすると、標準メモリ例外がスローされます。
ClassWizard は 、SQL_LONGVARCHAR または SQL_LONGVARBINARY を CLongBinary
にバインドします。 [メンバー変数の追加] ダイアログの [変数の種類] として CLongBinary
を選択します。 ClassWizard は、DoFieldExchange
呼び出しにRFX_LongBinary
呼び出しを追加し、バインドされたフィールドの合計数をインクリメントします。
長いデータ列の値を更新するには、まず、CLongBinary
のm_hData メンバーで ::GlobalSize を呼び出して、割り当てられたHGLOBAL
が新しいデータを保持するのに十分な大きさであることを確認します。 小さすぎる場合は、 HGLOBAL
を解放し、適切なサイズを割り当てます。 次 に、新 しいサイズを反映するようにm_dwDataLengthを設定します。
それ以外の場合、 m_dwDataLength が置き換えるデータのサイズよりも大きい場合は、 HGLOBAL
を解放して再割り当てするか、割り当てたままにすることができます。 m_dwDataLength で実際に使用されるバイト数を必ず指定してください。
CLongBinary の更新のしくみ
CLongBinary
の更新のしくみを理解する必要はありませんが、次に説明する 3 番目の方法を選択した場合、長いデータ値をデータ ソースに送信する方法の例として役立つ場合があります。
注
CLongBinary
フィールドを更新に含めるためには、フィールドのSetFieldDirty
を明示的に呼び出す必要があります。 Null の設定など、フィールドに変更を加えた場合は、 SetFieldDirty
を呼び出す必要があります。 また、2 番目のパラメーターが FALSE のSetFieldNull
を呼び出して、フィールドに値を設定するようにマークする必要があります。
CLongBinary
フィールドを更新する場合、データベース クラスは ODBC のDATA_AT_EXECメカニズムを使用します (SQLSetPos
の rgbValue 引数に関する ODBC ドキュメントを参照)。 データを含むHGLOBAL
を指す代わりに、フレームワークが挿入または更新ステートメントを準備すると、CLongBinary
のアドレスが列の値として設定され、長さのインジケーターがSQL_DATA_AT_EXECに設定されます。 後で、update ステートメントがデータ ソースに送信されると、 SQLExecDirect
は SQL_NEED_DATAを返します。 これにより、この列のパラメーターの値が実際には CLongBinary
のアドレスであることをフレームワークに警告します。 フレームワークは、ドライバーがデータの実際の長さを返すように期待して、小さなバッファーで 1 回 SQLGetData
を呼び出します。 ドライバーがバイナリ ラージ オブジェクト (BLOB) の実際の長さを返す場合、MFC は BLOB をフェッチするために必要なだけ領域を再割り当てします。 データ ソースが SQL_NO_TOTALを返し、BLOB のサイズを判断できないことを示す場合、MFC は小さなブロックを作成します。 既定の初期サイズは 64K で、後続のブロックはサイズの 2 倍になります。たとえば、2 つ目は 128K、3 つ目は 256K などです。 初期サイズは構成可能です。
バインドしない: SQLGetData を使用して ODBC から直接データを取得/送信する
このメソッドでは、データベース クラスを完全にバイパスし、長いデータ列を自分で処理します。
利点:
必要に応じてデータをディスクにキャッシュすることも、取得するデータの量を動的に決定することもできます。
短所:
フレームワークの Edit
や AddNew
のサポートは得られません。また、基本的な機能を実行するにはコードを自分で記述する必要があります (Delete
は、列レベルの操作ではないため機能します)。
この場合、長いデータ列はレコードセットの選択リストに含まれている必要がありますが、フレームワークによってバインドされないようにする必要があります。 これを行う 1 つの方法は、 GetDefaultSQL
を介して、または lpszSQL 引数として独自の SQL ステートメントを CRecordset
の Open
関数に渡し、追加の列をRFX_関数呼び出しにバインドしないことです。 ODBC では、バインドされていないフィールドがバインドされたフィールドの右側に表示される必要があるため、バインドされていない列を選択リストの末尾に追加します。
注
長いデータ列はフレームワークによってバインドされていないため、それに対する変更は CRecordset::Update
呼び出しでは処理されません。 必要な SQL INSERT ステートメントと UPDATE ステートメントを自分で作成して送信する必要があります。