次の方法で共有


TN045: Long Varchar/Varbinary の MFC/データベース サポート

次のテクニカル ノートは、最初にオンライン ドキュメントに含まれてから更新されていません。 その結果、一部の手順やトピックが古くなっているか、正しくない可能性があります。 最新情報については、オンライン ドキュメント インデックスで関心のあるトピックを検索することをお勧めします。

このノートでは、MFC データベース クラスを使用して ODBC SQL_LONGVARCHAR および SQL_LONGVARBINARY データ型を取得して送信する方法について説明します。

Long Varchar/Varbinary サポートの概要

ODBC SQL_LONG_VARCHARSQL_LONGBINARY データ型 (ここでは長いデータ列と呼ばれます) は、膨大な量のデータを保持できます。 このデータを処理する方法は 3 つあります。

  • CString / CByteArrayにバインドします。

  • CLongBinaryにバインドします。

  • データベース クラスに関係なく、それをバインドして長いデータ値を手動で取得して送信しないでください。

3 つの方法にはそれぞれ長所と短所があります。

長いデータ列は、クエリのパラメーターではサポートされていません。 これらは outputColumns でのみサポートされています。

長いデータ列を CString/CByteArray にバインドする

利点:

この方法は簡単に理解でき、使い慣れたクラスを使用します。 フレームワークでは、DDX_Textを使用したCStringCFormViewサポートが提供されます。 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_LONGVARCHARCStringにバインドするか、 SQL_LONGVARBINARYCByteArray にバインドします。 長いデータ列を取得する 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_LONGVARBINARYCLongBinary にバインドします。 [メンバー変数の追加] ダイアログの [変数の種類] として 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 番目のパラメーターが FALSESetFieldNullを呼び出して、フィールドに値を設定するようにマークする必要があります。

CLongBinary フィールドを更新する場合、データベース クラスは ODBC のDATA_AT_EXECメカニズムを使用します (SQLSetPosの rgbValue 引数に関する ODBC ドキュメントを参照)。 データを含むHGLOBALを指す代わりに、フレームワークが挿入または更新ステートメントを準備すると、CLongBinaryアドレスが列のとして設定され、長さのインジケーターがSQL_DATA_AT_EXECに設定されます。 後で、update ステートメントがデータ ソースに送信されると、 SQLExecDirectSQL_NEED_DATAを返します。 これにより、この列のパラメーターの値が実際には CLongBinaryのアドレスであることをフレームワークに警告します。 フレームワークは、ドライバーがデータの実際の長さを返すように期待して、小さなバッファーで 1 回 SQLGetData を呼び出します。 ドライバーがバイナリ ラージ オブジェクト (BLOB) の実際の長さを返す場合、MFC は BLOB をフェッチするために必要なだけ領域を再割り当てします。 データ ソースが SQL_NO_TOTALを返し、BLOB のサイズを判断できないことを示す場合、MFC は小さなブロックを作成します。 既定の初期サイズは 64K で、後続のブロックはサイズの 2 倍になります。たとえば、2 つ目は 128K、3 つ目は 256K などです。 初期サイズは構成可能です。

バインドしない: SQLGetData を使用して ODBC から直接データを取得/送信する

このメソッドでは、データベース クラスを完全にバイパスし、長いデータ列を自分で処理します。

利点:

必要に応じてデータをディスクにキャッシュすることも、取得するデータの量を動的に決定することもできます。

短所:

フレームワークの EditAddNew のサポートは得られません。また、基本的な機能を実行するにはコードを自分で記述する必要があります (Delete は、列レベルの操作ではないため機能します)。

この場合、長いデータ列はレコードセットの選択リストに含まれている必要がありますが、フレームワークによってバインドされないようにする必要があります。 これを行う 1 つの方法は、 GetDefaultSQL を介して、または lpszSQL 引数として独自の SQL ステートメントを CRecordsetOpen 関数に渡し、追加の列をRFX_関数呼び出しにバインドしないことです。 ODBC では、バインドされていないフィールドがバインドされたフィールドの右側に表示される必要があるため、バインドされていない列を選択リストの末尾に追加します。

長いデータ列はフレームワークによってバインドされていないため、それに対する変更は CRecordset::Update 呼び出しでは処理されません。 必要な SQL INSERT ステートメントと UPDATE ステートメントを自分で作成して送信する必要があります。

こちらも参照ください

番号別テクニカル ノート
カテゴリ別テクニカル ノート