複数のアクティブな結果セット (MARS) の使用

SQL Server 2005 では、データベース エンジンにアクセスするアプリケーションで複数のアクティブな結果セット (MARS) がサポートされるようになりました。以前のバージョンの SQL Server では、データベース アプリケーションは 1 つの接続で複数のアクティブなステートメントを保持できませんでした。SQL Server の既定の結果セットを使用しているときは、アプリケーションはその接続で他のバッチを実行する前に、1 つのバッチのすべての結果セットを処理するか、キャンセルする必要がありました。SQL Server 2005 では新しい接続属性が導入され、アプリケーションは接続ごとに複数の要求を保留中にできるだけでなく、接続ごとに複数のアクティブな既定の結果セットを保持できるようになりました。

MARS によって、次の新しい機能を使用したアプリケーションのデザインが簡素化されます。

  • アプリケーションは、既定の結果セットを複数開くことができ、複数の既定の結果セットからの読み取りをインターリーブすることができます。

  • アプリケーションは既定の結果セットを開いたまま、他のステートメント (INSERT、UPDATE、DELETE、ストアド プロシージャ呼び出しなど) を実行できます。

MARS を使用するアプリケーションについては、次のガイドラインを参照してください。

  • 1 つの SQL ステートメント (SELECT、OUTPUT を伴う DML、RECEIVE、READ TEXT など) で生成される結果セットが短期間しか有効でない場合や結果セットが小さい場合は、既定の結果セットを使用します。

  • 1 つの SQL ステートメントで生成される結果セットが長期間有効な場合や結果セットが大きくなる場合は、サーバー カーソルを使用します。

  • 結果を返すかどうかにかかわらず手続き型の要求の場合、および複数の結果を返すバッチの場合、常に、結果の最後まで読み取ります。

  • 接続プロパティの変更やトランザクションの管理には、できるだけ Transact-SQL ステートメントではなく API 呼び出しを優先して使用します。

  • MARS では、複数のバッチが同時に実行されている間、セッション スコープの権限の借用が禁止されます。

注意

既定では、MARS 機能は有効になっていません。SQL Server Native Client で SQL Server に接続するときに MARS を使用するには、接続文字列で MARS を明示的に有効にする必要があります。詳細については、この後の「SQL Server Native Client OLE DB プロバイダ」と「SQL Server Native Client ODBC ドライバ」を参照してください。

SQL Server Native Client では、接続でのアクティブなステートメントの数は制限されません。

複数のステートメントで構成される単一のバッチやストアド プロシージャを複数同時実行する必要のない一般的なアプリケーションでは、MARS の実装方法を理解しなくても、MARS の利点を得ることができます。ただし、より複雑な要件のアプリケーションでは、MARS の実装方法を考慮する必要があります。

MARS では、1 つの接続内で複数の要求の実行をインターリーブできます。つまり、1 つのバッチを実行し、その実行内で他の要求を実行できます。ただし、MARS では並列実行が行われるのではなく、複数の実行がインターリーブされることに注意してください。

MARS のインフラストラクチャでは、複数のバッチがインターリーブ方式で実行されますが、実行の切り替えは明確に定義された時点でしか行われません。また、ほとんどのステートメントはバッチ内で自動的に実行される必要があります。クライアントに行を返すステートメント (呼び出しポイントと呼ばれることもあります) では、行をクライアントへ送信しているとき、その送信を完了する前に実行をインターリーブすることができます。次に例を示します。

  • SELECT

  • FETCH

  • RECEIVE

ストアド プロシージャまたはバッチの一部として実行されるその他のステートメントはいずれも、他の MARS 要求に切り替えられる前に実行を完了する必要があります。

バッチがどのようにインターリーブ実行されるかは、さまざまな要因の影響を受けます。そのため、呼び出しポイントを含む複数のバッチからのコマンドが実行されるときに、正確なシーケンスを予測することは困難です。このような複雑なバッチをインターリーブ実行したときに、好ましくない影響が発生しないように注意してください。

このような問題を回避するために、接続状態 (SET、USE) やトランザクション (BEGIN TRAN、COMMIT、ROLLBACK) を管理する場合は、Transact-SQL ステートメントではなく API 呼び出しを使用します。このとき、複数のステートメントで構成されるバッチに呼び出しポイントも含まれている場合、接続状態やトランザクションを管理するステートメントを含めないようにします。さらに、このようなバッチでは、すべての結果を処理するか、残りをキャンセルすることによって、実行を順番に行います。

注意

MARS が有効なときにトランザクションを手動または暗黙に開始するバッチやストアド プロシージャでは、バッチが終了する前にトランザクションを完了する必要があります。トランザクションを完了しないと、SQL Server はバッチの終了時にトランザクションによって行われたすべての変更をロールバックします。このようなトランザクションは、SQL Server によってバッチスコープのトランザクションとして管理されます。これは SQL Server 2005 で導入された新しい形式のトランザクションで、MARS が有効なときに、既存の適切に動作するストアド プロシージャを使用できるようになります。バッチスコープのトランザクションの詳細については、「トランザクション ステートメント (Transact-SQL)」および「トランザクションの制御 (データベース エンジン)」を参照してください。

ADO から MARS を使用する場合の例については、「SQL Server Native Client と ADO の併用」を参照してください。

SQL Server Native Client OLE DB プロバイダ

SQL Server Native Client OLE DB プロバイダでは、DBPROPSET_SQLSERVERDBINIT プロパティ セットに実装される SSPROP_INIT_MARSCONNECTION データ ソース初期化プロパティの追加によって、MARS がサポートされます。また、新しい接続文字列のキーワードとして MarsConn が追加されました。このキーワードは、true または false を値として受け取ります。既定値は false です。

データ ソース プロパティ DBPROP_MULTIPLECONNECTIONS の既定値は VARIANT_TRUE です。これは、複数の同時実行コマンドや行セット オブジェクトをサポートするために、プロバイダが複数の接続を起動することを意味しています。MARS が有効なとき、SQL Server Native Client では 1 つの接続で複数のコマンドや行セット オブジェクトをサポートできるので、MULTIPLE_CONNECTIONS が既定で VARIANT_FALSE に設定されます。

DBPROPSET_SQLSERVERDBINIT プロパティ セットに行われた機能強化の詳細については、「初期化プロパティと承認プロパティ」を参照してください。

SQL Server Native Client OLE DB プロバイダの例

次の例では、セッション オブジェクトを作成する前に、SQL Server ネイティブ OLE DB プロバイダを使用してデータ ソース オブジェクトを作成し、DBPROPSET_SQLSERVERDBINIT プロパティ セットを使用して MARS を有効にしています。

#include <sqlncli.h>

IDBInitialize *pIDBInitialize = NULL;
IDBCreateSession *pIDBCreateSession = NULL;
IDBProperties *pIDBProperties = NULL;

// Create the data source object.
hr = CoCreateInstance(CLSID_SQLNCLI10, NULL,
   CLSCTX_INPROC_SERVER,
   IID_IDBInitialize, 
    (void**)&pIDBInitialize);

hr = pIDBInitialize->QueryInterface(IID_IDBProperties, (void**)&pIDBProperties);

// Set the MARS property.
DBPROP rgPropMARS;

// The following is necessary since MARS is off by default.
rgPropMARS.dwPropertyID = SSPROP_INIT_MARSCONNECTION;
rgPropMARS.dwOptions = DBPROPOPTIONS_REQUIRED;
rgPropMARS.dwStatus = DBPROPSTATUS_OK;
rgPropMARS.colid = DB_NULLID;
V_VT(&(rgPropMARS.vValue)) = VT_BOOL;
V_BOOL(&(rgPropMARS.vValue)) = VARIANT_TRUE;

// Create the structure containing the properties.
DBPROPSET PropSet;
PropSet.rgProperties = &rgPropMARS;
PropSet.cProperties = 1;
PropSet.guidPropertySet = DBPROPSET_SQLSERVERDBINIT;

// Get an IDBProperties pointer and set the initialization properties.
pIDBProperties->SetProperties(1, &PropSet);
pIDBProperties->Release();

// Initialize the data source object.
hr = pIDBInitialize->Initialize();

//Create a session object from a data source object.
IOpenRowset * pIOpenRowset = NULL;
hr = IDBInitialize->QueryInterface(IID_IDBCreateSession, (void**)&pIDBCreateSession));
hr = pIDBCreateSession->CreateSession(
   NULL,             // pUnkOuter
   IID_IOpenRowset,  // riid
  &pIOpenRowset ));  // ppSession

// Create a rowset with a firehose mode cursor.
IRowset *pIRowset = NULL;
DBPROP rgRowsetProperties[2];

// To get a firehose mode cursor request a 
// forward only read only rowset.
rgRowsetProperties[0].dwPropertyID = DBPROP_IRowsetLocate;
rgRowsetProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
rgRowsetProperties[0].dwStatus = DBPROPSTATUS_OK;
rgRowsetProperties[0].colid = DB_NULLID;
VariantInit(&(rgRowsetProperties[0].vValue));
rgRowsetProperties[0].vValue.vt = VARIANT_BOOL;
rgRowsetProperties[0].vValue.boolVal = VARIANT_FALSE;

rgRowsetProperties[1].dwPropertyID = DBPROP_IRowsetChange;
rgRowsetProperties[1].dwOptions = DBPROPOPTIONS_REQUIRED;
rgRowsetProperties[1].dwStatus = DBPROPSTATUS_OK;
rgRowsetProperties[1].colid = DB_NULLID;
VariantInit(&(rgRowsetProperties[1].vValue));
rgRowsetProperties[1].vValue.vt = VARIANT_BOOL;
rgRowsetProperties[1].vValue.boolVal = VARIANT_FALSE;

DBPROPSET rgRowsetPropSet[1];
rgRowsetPropSet[0].rgProperties = rgRowsetProperties
rgRowsetPropSet[0].cProperties = 2
rgRowsetPropSet[0].guidPropertySet = DBPROPSET_ROWSET;

hr = pIOpenRowset->OpenRowset (NULL,
   &TableID,
   NULL,
   IID_IRowset,
   1,
   rgRowsetPropSet
   (IUnknown**)&pIRowset);

SQL Server Native Client ODBC ドライバ

SQL Server Native Client ODBC ドライバでは、SQLSetConnectAttr 関数と SQLGetConnectAttr 関数を追加することで、MARS をサポートします。SQL_COPT_SS_MARS_ENABLED が追加され、SQL_MARS_ENABLED_YES または SQL_MARS_ENABLED_NO を受け取ります。既定値は SQL_MARS_ENABLED_NO です。また、新しい接続文字列のキーワードとして Mars_Connection が追加されました。このキーワードは、"yes" と "no" を値として受け取ります。既定値は "no" です。

SQL Server Native Client ODBC ドライバの例

次の例では、SQLDriverConnect 関数を呼び出してデータベースに接続する前に、SQLSetConnectAttr 関数を使用して MARS を有効にしています。接続が確立されると、2 つの SQLExecDirect 関数を呼び出し、同じ接続で 2 つの別の結果セットを作成します。

#include <sqlncli.h>

SQLSetConnectAttr(hdbc, SQL_COPT_SS_MARS_ENABLED, SQL_MARS_ENABLED_YES, SQL_IS_UINTEGER);
SQLDriverConnect(hdbc, hwnd, 
   "DRIVER=SQL Server Native Client 10.0;
   SERVER=(local);trusted_connection=yes;", SQL_NTS, szOutConn, 
   MAX_CONN_OUT, &cbOutConn, SQL_DRIVER_COMPLETE);

SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt1);
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt2);

// The 2nd execute would have failed with connection busy error if
// MARS were not enabled.
SQLExecDirect(hstmt1, L”SELECT * FROM Authors”, SQL_NTS);
SQLExecDirect(hstmt2, L”SELECT * FROM Titles”, SQL_NTS);

// Result set processing can interleave.
SQLFetch(hstmt1);
SQLFetch(hstmt2);