使用多个活动的结果集 (MARS)

SQL Server 2005 在访问数据库引擎的应用程序中引入了对多个活动结果集 (MARS) 的支持。在 SQL Server 的早期版本中,数据库应用程序无法在单个连接上保持多个活动语句。使用 SQL Server 默认结果集时,应用程序必须先处理或取消自某一批处理生成的所有结果集,然后才能执行该连接上的其他任何批处理。SQL Server 2005 引入了新连接属性,支持应用程序在每个连接上拥有多个待定请求,特别是支持每个连接具有多个活动的默认结果集。

MARS 通过以下新功能简化了应用程序设计:

  • 应用程序可以同时打开多个默认结果集,并且交错读取它们。

  • 应用程序可以在默认结果集打开的同时执行其他语句(例如 INSERT、UPDATE、DELETE 和存储过程调用)。

下列指南对使用 MARS 的应用程序很有帮助:

  • 默认结果集应该用于使用单个 SQL 语句(SELECT、带 OUTPUT 的 DML、RECEIVE、READ TEXT 等)生成的短期或较小结果集。

  • 服务器游标应该用于使用单个 SQL 语句生成的长期或较大结果集。

  • 对于过程请求(不论它们是否返回结果)以及返回多个结果的批处理,应始终读取到它们的结果的末尾。

  • 尽可能使用 API 调用(而不是 Transact-SQL 语句)更改连接属性和管理事务。

  • 在 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 是从交错执行而不是从并行执行的角度定义的。

MARS 基础结构允许以交错方式执行多个批处理,尽管只能在定义完善的时间点切换执行。此外,多数语句必须在同一批处理内以原子方式运行。向客户端返回行的语句(有时称为*“收获点”*)在完成前可以交错执行,同时向客户端发送行,例如:

  • SELECT

  • FETCH

  • RECEIVE

作为存储过程或批处理的一部分执行的任何其他语句必须运行完毕,之后才能切换到执行其他 MARS 请求。

多个批处理交错执行的确切方式受若干因素影响,很难预测包含收获点的多个批处理中的命令的确切执行顺序。注意避免交错执行此类复杂批处理所产生的意外负面影响。

要避免这些问题,可使用 API 调用而不是 Transact-SQL 语句来管理连接状态(SET、USE)和事务(BEGIN TRAN、COMMIT、ROLLBACK),方法是不在同样包含收获点的多语句批处理中包括这些语句,以及通过使用或取消所有结果来顺序执行此类批处理。

注意注意

启用 MARS 时,启动手动或隐式事务的批处理或存储过程必须完成该事务,之后批处理才能退出。如果不是这样,SQL Server 将在批处理完成时回滚该事务所做的所有更改。这种事务由 SQL Server 作为批范围的事务管理。这是 SQL Server 2005 新引入的事务类型,用于在启用 MARS 时支持使用功能良好的现有存储过程。有关批范围的事务的详细信息,请参阅事务语句 (Transact-SQL)控制事务(数据库引擎)

有关从 ADO 使用 MARS 的示例,请参阅将 ADO 用于 SQL Server Native Client

SQL Server Native Client OLE DB 访问接口

SQL Server Native Client OLE DB 访问接口通过添加 SSPROP_INIT_MARSCONNECTION 数据源初始化属性(在 DBPROPSET_SQLSERVERDBINIT 属性集中实现)支持 MARS。此外,还添加了新的连接字符串关键字 MarsConn。它接受值 true 或 false;默认值为 false。

数据源属性 DBPROP_MULTIPLECONNECTIONS 默认为 VARIANT_TRUE。这意味着访问接口将生成多个连接以支持多个并发命令和行集对象。启用 MARS 后,SQL Server Native Client 可以在单个连接上支持多个命令和行集对象,所以默认情况下 MULTIPLE_CONNECTIONS 设置为 VARIANT_FALSE。

有关针对 DBPROPSET_SQLSERVERDBINIT 属性集的增强功能的详细信息,请参阅初始化和授权属性

SQL Server Native Client OLE DB 访问接口示例

在本示例中,使用 SQL Server Native 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 驱动程序通过向 SQLSetConnectAttrSQLGetConnectAttr 函数添加内容来支持 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 驱动程序示例

在本示例中,SQLSetConnectAttr 函数用于在调用 SQLDriverConnect 函数以连接数据库之前启用 MARS。建立连接后,将调用两个 SQLExecDirect 函数,用来在同一连接上创建两个单独的结果集。

#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);