テクニカル ノート 45: MFC/データベースの Long Varchar/Varbinary 型のサポート
注意
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。 結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。 最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。
ここでは、MFC (Microsoft Foundation Class) データベース クラスを使った 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 の値は、MFC で SQLBindColumn 関数のローカル バッファーを設定するために使用されます。 これはデータを格納するためのローカル バッファーで、ODBC ドライバーが実際に返すデータの量には影響しません。 RFX_Text と RFX_Binary は、SQLFetch を使用して、1 度だけ呼び出しを実行してバックエンド データベースからデータを取得します。 1 回のフェッチで返すことができるデータ量の制限は、ODBC ドライバーごとに異なります。 この制限が 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 操作中は元のデータ値が保存されます。Update が呼び出されてデータ値の変更が検出され、DIRTY または NULL の値が列に設定されると、データの比較が行われます。
長いデータ列と CLongBinary の連結
長いデータ列のデータが MAXINT バイトを超える場合は、そのデータを CLongBinary に格納します。
利点
この方法では、長いデータ列全体を取得できます。
欠点
データはメモリに格納されます。 この方法でも、大きすぎるデータを扱うと非常にコストがかかります。 Update 操作でフィールドを取り込むには、連結されたデータ メンバーに対して SetFieldDirty 関数を呼び出します。
長いデータ列を CLongBinary に格納するときは、データベース クラスでデータ列の合計サイズを調べて、データ値全体を保持できるように HGLOBAL メモリ セグメントを割り当ててください。 データベース クラスでは、割り当てられた HGLOBAL メモリ セグメントに全データを格納します。
長いデータ列に必要なサイズをデータ ソースが返せない場合は、フレームワークによって例外 AFX_SQL_ERROR_SQL_NO_TOTAL がスローされます。 HGLOBAL メモリ セグメントの割り当てが失敗すると、標準メモリ例外がスローされます。
ClassWizard は、SQL_LONGVARCHAR または SQL_LONGVARBINARY を CLongBinary に連結します。 [メンバー変数の追加] ダイアログ ボックスで、変数の型として CLongBinary を選択してください。 ClassWizard によって、RFX_LongBinary 呼び出しが DoFieldExchange 呼び出しに追加され、連結されているフィールドの総数が 1 増やされます。
長いデータ列値を更新するには、最初に 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 が設定されます。 後で、更新ステートメントがデータ ソースに送られると、SQLExecDirect は SQL_NEED_DATA を返します。 これによって、フレームワークでは、その列のパラメーターの値が実際には CLongBinary のアドレスであることがわかります。 そこで、フレームワークでは、ドライバーからデータの実際の長さが返されるものと想定し、小さいバッファーを 1 つ用意して SQLGetData を 1 回呼び出します。 バイナリ ラージ オブジェクト (BLOB: Binary Large Object) の実際の長さがドライバーから返される場合は、MFC によって BLOB のフェッチに必要な領域が再度割り当てられます。 データ ソースで BLOB のサイズを特定できない場合は、SQL_NO_TOTAL が返されます。この場合は、MFC によって小さなブロックが複数作成されます。 最初のブロックの既定のサイズ 64 KB です。2 番目以降のブロックは、サイズが順に倍加します。たとえば、2 番目のブロックは 128 KB になり、3 番目のブロックは 256 KB になります。以降も同様です。 最初のブロックのサイズは変更できます。
バインドではありません。取得送信データ SQLGetData を ODBC から直接
この方法では、データベース クラスを使わずに、長いデータ列を直接扱います。
利点
必要に応じて、データをディスクにキャッシュできます。また取り出すデータの大きさを動的に決定できます。
欠点
フレームワークでサポートされている Edit や AddNew は使用できません。基本的な操作を行うコードを自分で作成する必要があります。ただし、Delete は、列レベルの操作ではないため使用できます。
この場合は、長いデータ列がレコードセットの選択リストにあることが条件ですが、フレームワークによって連結されている必要はありません。 これを実現する方法の 1 つは、GetDefaultSQL を使用して独自の SQL ステートメントを指定するか、あるいは CRecordset の Open 関数の引数 lpszSQL として指定し、余分な列を RFX_ 関数呼び出しで連結しないことです。 ODBC では、連結されていないフィールドは連結されているフィールドの右側にある必要があるため、連結されていない列は選択リストの最後に追加する必要があります。
注意
長いデータ列はフレームワークによって連結されないため、データ列に対する変更は CRecordset::Update を呼び出しても処理されません。 INSERT や UPDATE などの必要な SQL ステートメントを生成して対処する必要があります。