Verwenden mehrerer aktiver Resultsets (MARS) in SQL Server Native Client
Gilt für: SQL Server Azure SQL-Datenbank Azure SQL Managed Instance Azure Synapse Analytics Analytics Platform System (PDW)
Wichtig
SQL Server Native Client (SNAC) wird nicht ausgeliefert mit:
- SQL Server 2022 (16.x) und höhere Versionen
- SQL Server Management Studio 19 und spätere Versionen
Der SQL Server Native Client (SQLNCLI oder SQLNCLI11) und der Microsoft OLE DB-Legacyanbieter für SQL Server (SQLOLEDB) werden für neue Anwendungsentwicklungen nicht empfohlen.
Für neue Projekte verwenden Sie einen der folgenden Treiber:
Informationen zu SQLNCLI, das als Komponente der SQL Server-Datenbank-Engine (Versionen 2012 bis 2019) verfügbar ist, finden Sie in dieser Ausnahme für den Supportlebenszyklus.
Seit SQL Server 2005 (9.x) werden Multiple Active Result Sets (MARS) in Anwendungen unterstützt, die auf Datenbank-Engine zugreifen. In früheren Versionen von SQL Server konnten Datenbankanwendungen nicht mehrere aktive Anweisungen über eine Verbindung verwalten. Beim Verwenden von SQL Server-Standardresultsets musste die Anwendung alle Resultsets aus einem Batch verarbeiten oder abbrechen, bevor ein anderer Batch auf dieser Verbindung ausgeführt werden konnte. In SQL Server 2005 (9.x) wurde ein neues Verbindungsattribut eingeführt, das es Anwendungen ermöglicht, mehr als eine ausstehende Anforderung pro Verbindung und mehr als ein aktives Standardresultset pro Verbindung anzugeben.
MARS vereinfacht den Anwendungsentwurf mit den folgenden neuen Fähigkeiten:
Anwendungen können mehrere Standardresultsets geöffnet haben und die Lesevorgänge daraus verschachteln.
Anwendungen können bei geöffneten Standardresultsets andere Anweisungen ausführen (z. B. INSERT, UPDATE, DELETE und Aufrufe gespeicherter Prozeduren).
Für Anwendungen, die MARS verwenden, gelten die folgenden nützlichen Richtlinien:
Standardresultsets sollten für kurzlebige oder kurze Resultsets verwendet werden, die durch einzelne SQL-Anweisungen generiert werden (SELECT, DML mit OUTPUT, RECEIVE, READ TEXT usw.).
Servercursor sollten für längerlebige oder große Resultsets verwendet werden, die durch einzelne SQL-Anweisungen generierte werden.
Lesen Sie bei Batches, die mehrere Ergebnisse zurückgeben, und bei Prozeduranforderungen immer bis zum Ende der Results, unabhängig davon, ob Ergebnisse zurückgeben werden oder nicht.
Wo möglich, verwenden Sie anstelle von Transact-SQL-Anweisungen API-Aufrufe, um Verbindungseigenschaften zu ändern und Transaktionen zu verwalten.
In MARS wird ein Identitätswechsel im Bereich einer Sitzung verhindert, solange gleichzeitige Batches ausgeführt werden.
Hinweis
Standardmäßig ist die MARS-Funktionalität nicht vom Treiber aktiviert. Um MARS beim Herstellen einer Verbindung mit SQL Server mit SQL Server Native Client zu verwenden, müssen Sie MARS in einem Verbindungszeichenfolge speziell aktivieren. Einige Anwendungen können MARS jedoch standardmäßig aktivieren, wenn die Anwendung erkennt, dass der Treiber MARS unterstützt. Für diese Anwendungen können Sie MARS bei Bedarf im Verbindungszeichenfolge deaktivieren. Weitere Informationen finden Sie in den Abschnitten sql Server Native Client OLE DB-Anbieter und SQL Server Native Client ODBC-Treiberabschnitte weiter unten in diesem Thema.
Sql Server Native Client beschränkt nicht die Anzahl der aktiven Anweisungen für eine Verbindung.
Typische Anwendungen, die nicht mehr als einen einzelnen Batch mit mehreren Anweisungen oder gespeicherte Prozeduren gleichzeitig ausführen müssen, profitieren von MARS, ohne verstehen zu müssen, wie MARS implementiert wird. Anwendungen mit komplexeren Anforderungen müssen diese jedoch berücksichtigen.
MARS ermöglicht die verschachtelte Ausführung mehrerer Anforderungen innerhalb einer einzelnen Verbindung. Das bedeutet, dass innerhalb der Ausführung eines Batches eine weitere Anforderung ausgeführt werden kann. Beachten Sie jedoch, dass MARS mit Blick auf Interleaving, nicht die parallele Ausführung definiert ist.
Die MARS-Infrastruktur ermöglicht die verschachtelte Ausführung mehrerer Batches, die Ausführung kann jedoch nur an genau definierten Punkten gewechselt werden. Außerdem müssen die meisten Anweisungen innerhalb eines Batches atomar ausgeführt werden. Anweisungen, die Zeilen an den Client zurückgeben, die manchmal als Renditepunkte bezeichnet werden, dürfen die Ausführung vor Abschluss des Vorgangs zwischenspeichern, während Zeilen an den Client gesendet werden, z. B.:
SELECT
FETCH
RECEIVE
Alle anderen Anweisungen, die im Rahmen einer gespeicherten Prozedur oder eines Batches ausgeführt werden, müssen zunächst abgeschlossen werden, ehe die Ausführung zu anderen MARS-Anforderungen umgeschaltet werden kann.
Wie Batches die Ausführung genau verschachteln, hängt von zahlreichen Faktoren ab, und die exakte Ausführungsfolge von Befehlen aus mehreren Batches mit Zwischenergebnispunkten lässt sich nur schwer vorhersagen. Achten Sie darauf, unerwünschte Nebeneffekte aufgrund der verschachtelten Ausführung solcher komplexer Batches zu vermeiden.
Sie vermeiden Probleme, indem Sie den Verbindungsstatus (SET, USE) und Transaktionen (BEGIN TRAN, COMMIT, ROLLBACK) an Stelle von Transact-SQL-Anweisungen mit API-Aufrufen verwalten. Schließen Sie diese Anweisungen zudem nicht in Batches mit mehreren Anweisungen ein, die auch Zwischenergebnispunkte enthalten, und serialisieren Sie die Ausführung solcher Batches durch Verarbeitung oder Abbruch aller Ergebnisse.
Hinweis
Ein Batch oder eine gespeicherte Prozedur, die bei Aktivierung von MARS eine manuelle oder implizite Transaktion startet, muss diese Transaktion vor Ausführung des Batchs abschließen. Andernfalls führt SQL Server nach Abschluss des Batchs einen Rollback für alle von der Transaktion vorgenommenen Änderungen aus. Eine derartige Transaktion wird von SQL Server als Transaktion im Bereich des Batchs verwaltet. Dieser Transaktionstyp wurde in SQL Server 2005 (9.x) neu eingeführt, um vorhandene, gut konzipierte gespeicherte Prozeduren verwenden zu können, wenn MARS aktiviert ist. Weitere Informationen zu Transaktionen im Bereich des Batches finden Sie unter Transaktionsanweisungen (Transact-SQL).
Ein Beispiel für die Verwendung von MARS aus ADO finden Sie unter Verwenden von ADO mit SQL Server Native Client.
In-Memory-OLTP
In-Memory-OLTP unterstützt MARS mithilfe von Abfragen und systemintern kompilierten gespeicherten Prozeduren. MARS ermöglicht das Anfordern von Daten aus mehreren Abfragen, ohne dass vor dem Senden einer Anforderung zum Abrufen von Zeilen aus einem neuen Resultset jedes Resultset vollständig abgerufen werden muss. Um erfolgreich aus mehreren geöffneten Resultsets zu lesen, müssen Sie eine MARS-aktivierte Verbindung verwenden.
MARS ist standardmäßig deaktiviert, deshalb muss die Funktion durch Hinzufügen von MultipleActiveResultSets=True
zu einer Verbindungszeichenfolge explizit aktiviert werden. Das folgende Beispiel veranschaulicht, wie eine Verbindung mit einer SQL Server-Instanz hergestellt und wie angegeben wird, dass MARS aktiviert werden soll:
Data Source=MSSQL; Initial Catalog=AdventureWorks; Integrated Security=SSPI; MultipleActiveResultSets=True
MARS mit In-Memory-OLTP ist im Wesentlichen dasselbe wie MARS im Rest der SQL-Engine. In den folgenden Listen werden die Unterschiede bei der Verwendung von MARS in speicheroptimierten Tabellen und systemintern kompilierten gespeicherten Prozeduren unterstützt aufgeführt.
MARS und speicheroptimierte Tabellen
Im Folgenden werden die Unterschiede zwischen datenträgerbasierten und speicheroptimierten Tabellen bei Verwendung einer für MARS aktivierten Verbindung aufgeführt:
Zwei Anweisungen können Daten im selben Zielobjekt ändern. Wenn aber beide Anweisungen versuchen, denselben Datensatz zu ändern, führt ein write-write-Konflikt zu einem Vorgangsfehler. Werden durch die zwei Vorgänge hingegen verschiedene Datensätze geändert, sind die Vorgänge erfolgreich.
Jede Anweisung wird mit SNAPSHOT-Isolation ausgeführt, sodass neue Vorgänge die von vorhandenen Anweisungen durchgeführten Änderungen sehen können. Selbst wenn die parallel ausgeführten Anweisungen im Rahmen derselben Transaktion ausgeführt werden, erstellt die SQL-Engine für jede Anweisung Transaktionen im Bereich des Batches, die voneinander isoliert sind. Allerdings sind Transaktionen im Bereich des Batches weiterhin miteinander verbunden, sodass sich ein Rollback einer Transaktion im Bereich des Batches auf andere Transaktionen im selben Batch auswirkt.
DDL-Vorgänge sind in Benutzertransaktionen nicht zugelassen und führen sofort zu einem Fehler.
MARS und nativ kompilierte gespeicherte Prozeduren
Systemintern kompilierte gespeicherte Prozeduren können in für MARS aktivierten Verbindungen ausgeführt werden und die Ausführung nur dann an eine andere Anweisung abgeben, wenn ein Abgabepunkt ermittelt wird. Ein Abgabepunkt erfordert eine SELECT-Anweisung, bei der es sich um die einzige Anweisung innerhalb einer systemintern kompilierten gespeicherten Prozedur handelt, die die Ausführung an eine andere Anweisung abgeben kann. Wenn keine SELECT-Anweisung in der Prozedur enthalten ist, erfolgt keine Abgabe, und die Prozedurausführung wird abgeschlossen, bevor andere Anweisungen ausgeführt werden.
MARS und In-Memory-OLTP-Transaktionen
Änderungen, die durch Anweisungen und atomare Blöcke vorgenommen werden, die sich überlappen, sind voneinander isoliert. Wenn beispielsweise eine Anweisung oder ein atomarer Block einige Änderungen vornimmt und dann die Ausführung an eine andere Anweisung abgibt, sind die von der ersten Anweisung durchgeführten Änderungen für die neue Anweisung nicht sichtbar. Darüber hinaus sind bei der Wiederaufnahme der Ausführung durch die erste Anweisungen keine Änderungen durch andere Anweisungen sichtbar. Für Anweisungen sind nur Änderungen sichtbar, die vor Beginn der Anweisung abgeschlossen und bestätigt wurden.
Eine neue Benutzertransaktion kann mit der BEGIN TRANSACTION-Anweisung innerhalb der aktuellen Benutzertransaktion gestartet werden . Dies wird nur im Interoperabilitätsmodus unterstützt, sodass BEGIN TRANSACTION nur aus einer T-SQL-Anweisung aufgerufen werden kann und nicht innerhalb einer systemeigenen kompilierten gespeicherten Prozedur. Sie können einen Speicherpunkt in einer Transaktion mithilfe von SAVE TRANSACTION oder einem API-Aufruf der Transaktion erstellen. Save(save_point_name) to rollback to the savepoint. Diese Funktion wird ebenfalls nur aus T-SQL-Anweisungen aktiviert, und nicht aus systemintern kompilierten gespeicherten Prozeduren.
MARS und Columnstore-Indizes
SQL Server (ab Version 2016) unterstützt MARS mit Columnstore-Indizes. SQL Server 2014 verwendet MARS für schreibgeschützte Verbindungen mit Tabellen mit einem Columnstore-Index. SQL Server 2014 unterstützt MARS jedoch nicht für gleichzeitige DML-Vorgänge (Data Manipulation Language, Datenbearbeitungssprache) für eine Tabelle mit einem Columnstore-Index. In diesem Fall beendet SQL Server die Verbindung und bricht die Transaktionen ab. SQL Server 2012 umfasst schreibgeschützte Columnstore-Indizes, für die MARS nicht gilt.
SQL Server Native Client OLE DB-Anbieter
Der OLE DB-Anbieter von SQL Server Native Client unterstützt MARS durch das Hinzufügen der SSPROP_INIT_MARSCONNECTION Eigenschaft für die Datenquelleninitialisierung, die im DBPROPSET_SQLSERVERDBINIT-Eigenschaftensatz implementiert wird. Außerdem wurde ein neues Verbindungszeichenfolgen-Schlüsselwort, MarsConn, aufgenommen. Akzeptiert werden die Werte true oder false, false ist die Standardeinstellung.
Die Datenquelleneigenschaft DBPROP_MULTIPLECONNECTIONS ist standardmäßig auf VARIANT_TRUE festgelegt. Das bedeutet, der Anbieter erzeugt mehrere Verbindungen, um mehrere gleichzeitige Befehls- und Rowsetobjekte zu unterstützen. Wenn MARS aktiviert ist, kann SQL Server Native Client mehrere Befehls- und Rowsetobjekte für eine einzelne Verbindung unterstützen, sodass MULTIPLE_CONNECTIONS standardmäßig auf VARIANT_FALSE festgelegt ist.
Weitere Informationen zu Verbesserungen am DBPROPSET_SQLSERVERDBINIT-Eigenschaftensatz finden Sie unter Initialisierungs- und Autorisierungseigenschaften.
OLE DB-Anbieter von SQL Server Native Client: Beispiel
In diesem Beispiel wird ein Datenquellenobjekt mithilfe des SQL Server Native OLE DB-Anbieters erstellt, und MARS wird mithilfe des DBPROPSET_SQLSERVERDBINIT-Eigenschaftssatzes aktiviert, bevor das Sitzungsobjekt erstellt wird.
#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);
ODBC-Treiber für SQL Server Native Client
Der SQL Server Native Client ODBC-Treiber unterstützt MARS durch Ergänzungen der Funktionen SQLSetConnectAttr und SQLGetConnectAttr . SQL_COPT_SS_MARS_ENABLED wurde hinzugefügt, um entweder SQL_MARS_ENABLED_YES oder SQL_MARS_ENABLED_NO zu akzeptieren. Der Standardwert ist SQL_MARS_ENABLED_NO. Darüber hinaus wurde ein neues Verbindungszeichenfolge-Schlüsselwort Mars_Connection hinzugefügt. Gültige Werte sind "Ja" oder "Nein", wobei "Nein" die Standardeinstellung ist.
SQL Server Native Client-ODBC-Treiber: Beispiel
In diesem Beispiel wird die SQLSetConnectAttr-Funktion verwendet, um MARS zu aktivieren, bevor die SQLDriverConnect-Funktion aufgerufen wird, um die Datenbank zu verbinden. Nachdem die Verbindung hergestellt wurde, werden zwei SQLExecDirect-Funktionen aufgerufen, um zwei separate Resultsets für dieselbe Verbindung zu erstellen.
#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);
Weitere Informationen
SQL Server Native Client-Funktionen
Verwenden von SQL Server-Standardresultsets