TN045: MFC/データベースの Long Varchar/Varbinary 型のサポート
Note
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。 結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。 最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。
このメモでは、MFC データベースクラスを使用して、ODBC SQL_LONGVARCHAR および SQL_LONGVARBINARY データ型を取得して送信する方法について説明します。
Long Varchar/Varbinary サポートの概要
ODBC SQL_LONG_VARCHAR および SQL_LONGBINARY データ型 (長いデータ列と呼ばれます) では、大量のデータを保持できます。 このデータを処理するには、次の3つの方法があります:
これを
CString
/CByteArray
にバインドします。これを
CLongBinary
にバインドします。データベース クラスに関係なく、すべてをバインドして、長いデータ値を手動で取得して送信することは避けてください。
これら 3 つの方法にはそれぞれ長所と短所があります。
長いデータ列は、クエリのパラメーターではサポートされていません。 これらは outputColumns でのみサポートされています。
Long データ列を CString/CByteArray にバインドする
長所:
この方法はわかりやすく、使い慣れたクラスを使用して作業します。 フレームワークは、DDX_Text
を使用した CFormView
による CString
のサポートを提供 します。 CString
クラスと CByteArray
クラスを使用した一般的な文字列またはコレクションの機能が多数あり、データ値を保持するためにローカルに割り当てられるメモリの量を制御できます。 フレームワークでは、Edit
または AddNew
関数の呼び出し中にフィールドデータの古いコピーが保持され、データに対する変更はフレームワークによって自動的に検出されます。
Note
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);
Long データ列を CString
または CByteArray
に取得した場合、返されるデータの最大量は、既定では 255 バイトになります。 これを超えるものは無視されます。 この場合、フレームワークは AFX_SQL_ERROR_DATA_TRUNCATED 例外をスローします。 幸い、nMaxLength をより大きな値にするには、Maxint まで明示的に増やすことができます。
Note
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
にデータ値全体を取得します。
データソースが long データ列の予想サイズを返すことができない場合、フレームワークは AFX_SQL_ERROR_SQL_NO_TOTAL 例外をスローします。 HGLOBAL
を割り当てようとして失敗した場合は、標準メモリ例外がスローされます。
ClassWizard は、 SQL_LONGVARCHAR または SQL_LONGVARBINARY を CLongBinary
にバインドします。 [メンバー変数の追加] ダイアログボックスで変数の型として CLongBinary
を選択します。 その後、ClassWizard は RFX_LongBinary
呼び出しを DoFieldExchange
呼び出しに追加し、バインドされたフィールドの合計数を増やします。
長いデータ列の値を更新するには、まず、CLongBinary
の m_hData メンバーに対して ::GlobalSize を呼び出して、割り当てられた HGLOBAL
が新しいデータを保持するのに十分な大きさであることを確認します。 小さすぎる場合は、 HGLOBAL
を解放し、適切なサイズで割り当てます。 次に、新しいサイズを反映するように m_dwDataLength を設定します。
それ以外の場合、m_dwDataLength が置換するデータのサイズより大きい場合は、 HGLOBAL
を解放して再割り当てするか、割り当てたままにしておくことができます。 m_dwDataLength で実際に使用されているバイト数を必ず指定してください。
CLongBinary の更新のしくみ
CLongBinary
を更新する方法を理解する必要はありませんが、次に説明する 3 番目の方法を選択した場合、データソースに長いデータ値を送信する方法の例として役立つことがあります。
Note
CLongBinary
フィールドを更新に含めるには、フィールドに対して SetFieldDirty
を明示的にを呼び出す必要があります。 Null の設定など、フィールドに変更を加える場合は SetFieldDirty
を呼び出す必要があります。 また、SetFieldNull
を呼び出して、2 番目のパラメーターを FALSE に設定し、フィールドを値としてマークする必要があります。
CLongBinary
フィールドを更新する場合、データベースクラスは ODBC の DATA_AT_EXEC 機構を使用します (SQLSetPos
の rgbValue 引数に関する ODBC ドキュメントを参照してください)。 フレームワークが insert ステートメントまたは update ステートメントを準備するときに、データを含む HGLOBAL
をポイントするのではなく、CLongBinary
の アドレス が列の 値 として設定され、長さインジケーターが SQL_DATA_AT_EXEC に設定されます。 その後、update ステートメントがデータソースに送信されると、SQLExecDirect
は SQL_NEED_DATA を返します。 これは、この列の param の値が実際に CLongBinary
のアドレスであることをフレームワークに通知します。 フレームワークは、小さなバッファーを使用して SQLGetData
を 1 回呼び出し、ドライバーがデータの実際の長さを返すことを想定しています。 ドライバーがバイナリ ラージ オブジェクト (BLOB) の実際の長さを返す場合、MFC は BLOB をフェッチするために必要なだけ領域を再割り当てします。 データソースが SQL_NO_TOTAL を返し、BLOB のサイズを特定できないことを示している場合、MFC は小さいブロックを作成します。 既定の初期サイズは 64K で、後続のブロックは 2 倍のサイズになります。たとえば、2 番目のは128K、3 番目は256K、などです。 初期サイズは構成可能です。
バインドされていません: SQLGetData を使用して ODBC から直接データを取得/送信します
この方法では、データベース クラスを完全にバイパスし、長いデータ列を自分で処理します。
長所:
必要に応じてデータをディスクにキャッシュしたり、取得するデータ量を動的に決定したりすることができます。
短所:
フレームワークの Edit
または AddNew
のサポートがないため、基本的な機能を実行するためにコードを自分で記述する必要があります (これは列レベルの操作ではないため、Delete
が動作します)。
この場合、長いデータ列はレコードセットの選択リストに含まれている必要がありますが、フレームワークによってバインドされることはできません。 これを行う 1 つの方法として、独自の SQL ステートメント GetDefaultSQL
経由で提供するか、CRecordset
の Open
関数の lpszSQL 引数として指定したり、RFX_ 関数呼び出しを使用して余分な列をバインドしないようにするやり方があります。 ODBC では、バインドされたフィールドの右側にバインドされていないフィールドが表示されるため、バインドされていない列を選択リストの末尾に追加します。
Note
長いデータ列はフレームワークによってバインドされていないため、この列への変更は CRecordset::Update
の呼び出しでは処理されません。 必要な SQL INSERT および UPDATE ステートメントを自分で作成して送信する必要があります。