Gebruik van meerdere actieve resultaatsets (MARS) in de native client van SQL Server

van toepassing op:SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceAzure Synapse AnalyticsAnalytics Platform System (PDW)

Belangrijk

SQL Server Native Client (SNAC) wordt niet geleverd met:

  • SQL Server 2022 (16.x) en latere versies
  • SQL Server Management Studio 19 en latere versies

De SQL Server Native Client (SQLNCLI of SQLNCLI11) en de verouderde Microsoft OLE DB-provider voor SQL Server (SQLOLEDB) worden niet aanbevolen voor de ontwikkeling van nieuwe toepassingen.

Gebruik een van de volgende stuurprogramma's voor nieuwe projecten:

Voor SQLNCLI die als onderdeel van SQL Server Database Engine (versies 2012 tot en met 2019) wordt geleverd, raadpleegt u deze uitzondering voor de levenscyclus van ondersteuning.

SQL Server 2005 (9.x) introduceerde ondersteuning voor meerdere actieve resultaatsets (MARS) in applicaties die toegang hadden tot de Database Engine. In eerdere versies van SQL Server konden databaseapplicaties geen meerdere actieve statements op een verbinding onderhouden. Bij het gebruik van standaard resultaten in SQL Server moest de applicatie alle resultaatsets van één batch verwerken of annuleren voordat een andere batch op die verbinding kon worden uitgevoerd. SQL Server 2005 (9.x) introduceerde een nieuw verbindingsattribuut waarmee applicaties meer dan één wachtend verzoek per verbinding kunnen hebben, en in het bijzonder om meer dan één actieve standaardresultaat per verbinding te laten instellen.

MARS vereenvoudigt applicatieontwerp met de volgende nieuwe mogelijkheden:

  • Applicaties kunnen meerdere standaard resultaatsets open hebben en kunnen lezen uit deze door elkaar veroverd.

  • Applicaties kunnen andere statements uitvoeren (bijvoorbeeld INSERT, UPDATE, DELETE, en stored procedure-aanroepen) terwijl standaardresultatensets open zijn.

Applicaties die MARS gebruiken, zullen de volgende richtlijnen nuttig vinden:

  • Standaard resultaatsets moeten worden gebruikt voor kortstondige of korte resultaatsets die worden gegenereerd door enkele SQL-instructies (SELECT, DML met OUTPUT, RECEIVE, READ TEXT, enzovoort).

  • Servercursors moeten worden gebruikt voor langere of grote resultatensets die worden gegenereerd door enkele SQL-instructies.

  • Lees altijd het einde van resultaten voor procedurele verzoeken, ongeacht of ze resultaten teruggeven of niet, en voor batches die meerdere resultaten teruggeven.

  • Gebruik waar mogelijk API-aanroepen om verbindingseigenschappen te wijzigen en transacties te beheren in plaats van Transact-SQL statements.

  • In MARS is session-scoped imitatie verboden terwijl gelijktijdige batches draaien.

Opmerking

Standaard wordt MARS-functionaliteit niet ingeschakeld door de driver. Om MARS te gebruiken bij het verbinden met SQL Server met SQL Server Native Client, moet je MARS specifiek inschakelen binnen een verbindingsreeks. Sommige applicaties kunnen echter MARS standaard inschakelen als de applicatie detecteert dat de driver MARS ondersteunt. Voor deze toepassingen kun je MARS in de verbindingsreeks uitschakelen indien nodig. Voor meer informatie, zie de secties SQL Server Native Client OLE DB provider en SQL Server Native Client ODBC driver, later in dit onderwerp.

SQL Server Native Client beperkt het aantal actieve statements op een verbinding niet.

Typische applicaties die niet meer dan één multi-statement batch of opgeslagen procedure tegelijk hoeven uitvoeren, profiteren van MARS zonder te hoeven begrijpen hoe MARS wordt geïmplementeerd. Toepassingen met complexere vereisten moeten hier echter rekening mee houden.

MARS maakt de interlaced uitvoering van meerdere verzoeken binnen één verbinding mogelijk. Dat wil zeggen, het laat een batch draaien, en tijdens de uitvoering ervan laat het andere verzoeken uitvoeren. Let echter op dat MARS wordt gedefinieerd in termen van interleaving, niet in termen van parallelle uitvoering.

De MARS-infrastructuur maakt het mogelijk dat meerdere batches op interleaved wijze kunnen worden uitgevoerd, hoewel de uitvoering alleen op goed gedefinieerde punten kan worden geschakeld. Daarnaast moeten de meeste statements atomair binnen een batch draaien. Statements die rijen teruggeven aan de client, soms aangeduid als yield points, mogen uitvoeringen interleaven voordat ze voltooid zijn terwijl rijen naar de client worden gestuurd, bijvoorbeeld:

  • SELECT

  • FETCH

  • RECEIVE

Alle andere statements die worden uitgevoerd als onderdeel van een opgeslagen procedure of batch moeten volledig worden uitgevoerd voordat uitvoering kan worden overgeschakeld naar andere MARS-verzoeken.

De exacte manier waarop batch-interleave-uitvoering wordt beïnvloed door een aantal factoren, en het is moeilijk om de exacte volgorde te voorspellen waarin commando's van meerdere batches met yield points zullen worden uitgevoerd. Wees voorzichtig om ongewenste bijwerkingen te vermijden door de interleaved uitvoering van zulke complexe batches.

Vermijd problemen door API-aanroepen te gebruiken in plaats van Transact-SQL statements om de verbindingsstatus (SET, USE) en transacties (BEGIN TRAN, COMMIT, ROLLBACK) te beheren door deze statements niet op te nemen in multi-statement batches die ook yield points bevatten, en door de uitvoering van dergelijke batches te serialiseren door alle resultaten te consumeren of te annuleren.

Opmerking

Een batch- of opgeslagen procedure die een handmatige of impliciete transactie start wanneer MARS wordt ingeschakeld, moet de transactie voltooien voordat de batch wordt afgesloten. Als dat niet gebeurt, rolt SQL Server alle wijzigingen die door de transactie zijn aangebracht terug zodra de batch is afgerond. Zo'n transactie wordt door SQL Server beheerd als een batch-scoped transactie. Dit is een nieuw type transactie dat in SQL Server 2005 (9.x) is geïntroduceerd om bestaande goed gedragen opgeslagen procedures te kunnen gebruiken wanneer MARS is ingeschakeld. Voor meer informatie over batch-scoped transacties, zie Transaction Statements (Transact-SQL).

Voor een voorbeeld van het gebruik van MARS vanuit ADO, zie Using ADO with SQL Server Native Client.

In-memory OLTP

OLTP ondersteunt MARS met queries en native gecompileerde opgeslagen procedures. MARS maakt het mogelijk om gegevens van meerdere queries op te vragen zonder dat je elke resultaatset volledig hoeft op te halen voordat een verzoek wordt gestuurd om rijen uit een nieuwe resultaatset op te halen. Om succesvol uit meerdere open resultaatsets te lezen, moet je een MARS-geactiveerde verbinding gebruiken.

MARS is standaard uitgeschakeld, dus je moet het expliciet inschakelen door toe te voegen MultipleActiveResultSets=True aan een verbindingsstring. Het volgende voorbeeld laat zien hoe je verbinding maakt met een instantie van SQL Server en aangeeft dat MARS is ingeschakeld:

Data Source=MSSQL; Initial Catalog=AdventureWorks; Integrated Security=SSPI; MultipleActiveResultSets=True  

MARS met In-Memory OLTP is in wezen hetzelfde als MARS in de rest van de SQL-engine. Het volgende geeft een overzicht van de verschillen bij het gebruik van MARS in geheugengeoptimaliseerde tabellen en native gecompileerde opgeslagen procedures.

MARS- en geheugengeoptimaliseerde tabellen

De volgende verschillen tussen schijfgebaseerde en geheugengeoptimaliseerde tabellen bij gebruik van een MARS-geschikte verbinding:

  • Twee statements kunnen data wijzigen in hetzelfde doelobject, maar als ze allebei proberen hetzelfde record te wijzigen, zal een schrijf-schrijfconflict ervoor zorgen dat de nieuwe bewerking mislukt. Als beide bewerkingen echter verschillende records wijzigen, slagen de bewerkingen.

  • Elke instructie draait onder SNAPSHOT-isolatie, zodat nieuwe bewerkingen de wijzigingen van bestaande statements niet kunnen zien. Zelfs als de gelijktijdige statements onder dezelfde transactie worden uitgevoerd, maakt de SQL-engine batch-scoped transacties voor elke instructie die van elkaar geïsoleerd zijn. Echter, batch-scoped transacties blijven aan elkaar gebonden, dus het terugdraaien van één batch-scoped transactie beïnvloedt andere transacties in dezelfde batch.

  • DDL-bewerkingen zijn niet toegestaan in gebruikerstransacties, dus ze zullen direct falen.

MARS en native gecompileerde opgeslagen procedures

Native gecompileerde opgeslagen procedures kunnen draaien in MARS-enabled verbindingen en kunnen alleen uitvoering geven aan een andere instructie wanneer een yield point wordt gevonden. Een yield point vereist een SELECT-statement, die de enige statement is binnen een native gecompileerde stored procedure die uitvoering kan geven aan een andere statement. Als er geen SELECT-instructie aanwezig is in de procedure die deze niet oplevert, zal deze tot voltooiing draaien voordat andere statements beginnen.

MARS- en in-memory OLTP-transacties

Wijzigingen die door statements en atomaire blokken worden aangebracht die met elkaar verveven zijn, worden van elkaar geïsoleerd. Als bijvoorbeeld één statement of atomic block enkele wijzigingen aanbrengt en vervolgens uitvoering geeft aan een andere statement, zal de nieuwe statement geen wijzigingen zien die door de eerste statement zijn gemaakt. Bovendien zal de eerste instructie bij hervatting van de uitvoering geen wijzigingen zien die door andere instructies zijn aangebracht. Statements zien alleen wijzigingen die zijn afgerond en uitgevoerd voordat de verklaring begint.

Een nieuwe gebruikerstransactie kan binnen de huidige gebruikerstransactie worden gestart met de BEGIN TRANSACTION-instructie - dit wordt alleen ondersteund in interoperabiliteitsmodus, dus de BEGIN TRANSACTION kan alleen worden aangeroepen vanuit een T-SQL-instructie, en niet vanuit een native gecompileerde opgeslagen procedure. Je kunt een save point maken in een transactie met SAVE TRANSACTION of een API-aanroep naar transactie. Save(save_point_name) om terug te rollen naar het savepoint. Deze functie wordt ook alleen ingeschakeld vanuit T-SQL-instructies, en niet vanuit native gecompileerde opgeslagen procedures.

MARS- en columnstore-indexen

SQL Server (vanaf 2016) ondersteunt MARS met columnstore-indexen. SQL Server 2014 gebruikt MARS voor alleen-lezen verbindingen met tabellen met een columnstore-index. SQL Server 2014 ondersteunt echter geen MARS voor gelijktijdige data manipulation language (DML)-operaties op een tabel met een kolomopslagindex. Wanneer dit gebeurt, zal SQL Server de verbindingen beëindigen en de transacties afbreken. SQL Server 2012 heeft alleen-lezen kolomopslagindexen en MARS is daar niet op van toepassing.

SQL Server Native Client OLE DB-provider

De SQL Server Native Client OLE DB-provider ondersteunt MARS door de toevoeging van de SSPROP_INIT_MARSCONNECTION database source initialisatie-eigenschap, die is geïmplementeerd in de DBPROPSET_SQLSERVERDBINIT property set. Daarnaast is er een nieuw verbindingsstring-sleutelwoord toegevoegd, MarsConn. Het accepteert ware of onwaarse waarden; False is de standaard.

De eigenschap van de gegevensbron DBPROP_MULTIPLECONNECTIONS standaard op VARIANT_TRUE. Dit betekent dat de provider meerdere verbindingen zal opzetten om meerdere gelijktijdige commando- en rowset-objecten te ondersteunen. Wanneer MARS is ingeschakeld, kan SQL Server Native Client meerdere commando- en rowset-objecten op één verbinding ondersteunen, dus MULTIPLE_CONNECTIONS standaard op VARIANT_FALSE staat.

Voor meer informatie over verbeteringen aan de DBPROPSET_SQLSERVERDBINIT eigenschapsset, zie Initialisatie- en Autorisatie-eigenschappen.

SQL Server Native Client OLE DB Provider Voorbeeld

In dit voorbeeld wordt een databronobject gemaakt met de SQL Server Native OLE DB-provider, en wordt MARS ingeschakeld met de DBPROPSET_SQLSERVERDBINIT property set voordat het sessieobject wordt aangemaakt.

#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 Driver

De SQL Server Native Client ODBC-driver ondersteunt MARS door toevoegingen aan de SQLSetConnectAttr en SQLGetConnectAttr functies. SQL_COPT_SS_MARS_ENABLED is toegevoegd om ofwel SQL_MARS_ENABLED_YES of SQL_MARS_ENABLED_NO te accepteren, waarbij SQL_MARS_ENABLED_NO standaard is. Daarnaast is er een nieuw verbindingsreeks trefwoord toegevoegd, Mars_Connection. Het accepteert "ja" of "nee" waarden; "Nee" is de standaard.

SQL Server Native Client ODBC Driver voorbeeld

In dit voorbeeld wordt de SQLSetConnectAttr-functie gebruikt om MARS in te schakelen voordat de SQLDriverConnect-functie wordt aangeroepen om de database te verbinden. Zodra de verbinding is gemaakt, worden twee SQLExecDirect-functies aangeroepen om twee aparte resultaatsets op dezelfde verbinding te maken.

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

Zie ook

systeemeigen SQL Server-clientfuncties
Gebruik van SQL Server Standaardresultaten