7

MFC(VS2010)のCDatabase/CRecordsetを介してODBCを使用するアプリケーションがあります。2つのバックエンドが実装されています。MSSQLとMySQL。

現在、MSSQL(Native Client 10.0で)を使用する場合、SELECTを使用したレコードの取得は低速リンク(VPNなど)を介して劇的に遅くなります。MySQL ODBCドライバーは、この厄介な動作を示しません。

例えば:

CRecordset r(&m_db);
r.Open(CRecordset::snapshot, L"SELECT a.something, b.sthelse FROM TableA AS a LEFT JOIN TableB AS b ON a.ID=b.Ref");
r.MoveFirst();
while(!r.IsEOF())
{
    // Retrieve
    CString strData;
    crs.GetFieldValue(L"a.something", strData);
    crs.MoveNext();
}

これで、MySQLドライバーを使用すると、すべてが正常に実行されます。クエリが返され、すべてが非常に高速になります。ただし、MSSQL Native Clientでは、すべてのMoveNext()でドライバーがサーバーと通信するため、処理速度が低下します。

サーバー側のカーソルが原因だと思いますが、無効にする方法が見つかりませんでした。私は使用してみました:

::SQLSetConnectAttr(m_db.m_hdbc, SQL_ATTR_ODBC_CURSORS, SQL_CUR_USE_ODBC, SQL_IS_INTEGER);

しかし、これも役に立ちませんでした。SQLプロファイラーにはまだsp_cursorfetch()などの実行時間の長いexecがあります。SQLAPIとバルクフェッチを使用した小さな参照プロジェクトも試しましたが、FetchNext()でも長時間ハングします(結果セットにレコードが1つしかない場合でも)。ただし、これはLEFT JOINS、テーブル値関数などを使用したクエリでのみ発生し ます。クエリはそれほど長くはかからないことに注意してください。同じ接続を介してSQL Studioを介して同じSQLを実行すると、妥当な時間で返されます。

質問1:どういうわけかネイティブクライアントを取得することは可能ですかすべての結果をローカルに「キャッシュ」するMySQLドライバーが行うように見えるのと同様の方法でローカルカーソルを使用しますか?

たぶんこれは完全に間違ったアプローチですが、他にこれを行う方法がわかりません。

必要なのは、SELECTから一度にすべてのデータを取得し、次のクエリまでサーバーと通信しないことです。レコードセットの更新、削除など、またはそのナンセンスについては気にしません。データを取得したいだけです。そのレコードセットを取得し、すべてのデータを取得して削除します。

質問2:ODBCを使用してMFCでデータを取得するためのより効率的な方法はありますか?

4

1 に答える 1

3

私はもう少し問題をいじくり回して、これらの2つのリンクを見つけました:

MSDNリンク

MSDNブログ

最初のリンクでは、デフォルトのオプションが変更された場合にのみ、サーバー側カーソルがNativeClient10によって使用されることを説明しています。

SQLステートメントの実行時にこれらのオプションがデフォルトに設定されている場合、SQLServerネイティブクライアントODBCドライバーはサーバーカーソルを使用して結果セットを実装しません。代わりに、デフォルトの結果セットを使用します。

リンク2は、SQL Devブログであるブログであり、次のように述べています。

開発者がサーバーカーソルを明示的に要求しなかったことが判明しました。しかし、彼がブロックフェッチを行ったとき、副作用として、SQL Server ODBCドライバーがサーバーカーソルを要求しました...これは予想外です!

はい、確かにそれは予想外です...

サーバーカーソルの代わりにデフォルトの結果セット(消防ホースカーソル)でブロックフェッチを行うにはどうすればよいですか?

さて、ソリューションの実装はこれです:

それ以外の:

// crs is a CRecordSet
crs.Open(CRecordset::snapshot, L"SELECT something...");

これを行う:

// crs is a CRecordSet
crs.Open(CRecordset::forwardOnly, L"SELECT something...");

この単純な変更は、サーバー側カーソルの作成をトリガーせず、MySQLドライバーの動作を模倣します。

欠点は、(Microsoftが推奨する)方法で行数を取得できないことです。

while(crs.MoveNext()) nCount++;

とにかくこれは悪い考えです。また、:: SQLGetRowCount()は常に機能しなくなります。

私はこの問題を次のように解決しました(これはANSI互換のSQLソースで機能するはずです):

//strQuery is the random query passed to CountRows()
std::wstringstream ssQuery;
ssQuery << L"SELECT COUNT(*) AS Count FROM (" << strQuery << L") AS t";
CRecordset crs(&m_Database);
crs.Open(CRecordset::forwardOnly, ssQuery.str().c_str());
// Now retrieve the only "Count" field from the recordset.

これが将来誰か他の人に役立つことを願っています。

于 2012-10-14T20:57:11.890 に答える