Transaktionen in MSSQL fassen mehrere Datenbankänderungen zu einer Einheit zusammen. Das ist etwa bei einer Bestellung wichtig: Der Lagerbestand soll nur sinken, wenn die Bestellung tatsächlich gespeichert wird. Umgekehrt darf keine Bestellung entstehen, wenn der Bestand nicht ausreicht. Wie du BEGIN TRANSACTION, COMMIT und ROLLBACK dafür einsetzt und Fehler sauber behandelst, zeigt dieser Beitrag mit einem vollständigen T-SQL-Beispiel.

Was sind Transaktionen in MSSQL?

Eine Transaktion umfasst zusammengehörige Anweisungen. Mit COMMIT TRANSACTION bestätigst du die Änderungen; mit ROLLBACK TRANSACTION machst du sie rückgängig. Ohne ausdrücklich gestartete Transaktion führt SQL Server eine einzelne Anweisung üblicherweise als eigene Transaktion aus. Das sogenannte Autocommit reicht aber nicht, wenn mehrere Anweisungen nur gemeinsam gültig sind.

Das Grundmuster sieht so aus:

BEGIN TRANSACTION;

-- Zusammengehörige Änderungen ausführen.

COMMIT TRANSACTION;
-- Bei einem Abbruch stattdessen: ROLLBACK TRANSACTION;

Ein ROLLBACK nach einem bereits erfolgten COMMIT kann diese Transaktion nicht zurückholen. Für Wiederherstellung nach einem später entdeckten Fehler brauchst du eine neue Korrekturtransaktion oder ein geeignetes Wiederherstellungskonzept.

ACID: Welche Eigenschaften sollen Transaktionen erfüllen?

  • Atomicity (Atomarität): Die Änderungen werden gemeinsam bestätigt oder gemeinsam zurückgenommen.
  • Consistency (Konsistenz): Die Transaktion soll die Daten von einem gültigen Zustand in einen anderen überführen. Dafür brauchst du passende Constraints und fachliche Prüfungen; eine Transaktion allein kennt deine Geschäftsregeln nicht.
  • Isolation: Gleichzeitige Transaktionen beeinflussen sich nach den Regeln der gewählten Isolationsstufe. Welche Zwischenstände sichtbar sind, hängt von dieser Einstellung ab.
  • Durability (Dauerhaftigkeit): Bestätigte Änderungen überstehen im normalen dauerhaften Betriebsmodus einen Neustart. Sonderkonfigurationen wie verzögerte Dauerhaftigkeit haben abweichende Eigenschaften.

Gerade die zweite Eigenschaft wird häufig missverstanden: Wenn ein UPDATE keine Zeile findet, ist das nicht automatisch ein SQL-Fehler. Deine Anwendung muss prüfen, ob das Ergebnis fachlich stimmt.

Transaktionen in MSSQL: Bestellung und Lagerbestand als Beispiel

Das folgende Skript ist für eine Testumgebung gedacht. Es verwendet temporäre Tabellen, damit du keine vorhandenen Geschäftstabellen verändern musst. Führe den gesamten Block in einem Abfragefenster aus. Bei einem zweiten Durchlauf werden die Demo-Tabellen derselben Sitzung neu erstellt.

DROP TABLE IF EXISTS #TransDemoBestellungen;
DROP TABLE IF EXISTS #TransDemoLager;

CREATE TABLE #TransDemoLager (
    ArtikelID int NOT NULL PRIMARY KEY,
    Bestand int NOT NULL CHECK (Bestand >= 0)
);

CREATE TABLE #TransDemoBestellungen (
    BestellungID int IDENTITY(1,1) NOT NULL PRIMARY KEY,
    ArtikelID int NOT NULL,
    Menge int NOT NULL CHECK (Menge > 0)
);

INSERT INTO #TransDemoLager (ArtikelID, Bestand)
VALUES (456, 10);

SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;

    UPDATE #TransDemoLager
    SET Bestand = Bestand - 2
    WHERE ArtikelID = 456
      AND Bestand >= 2;

    IF @@ROWCOUNT <> 1
        THROW 50001, N'Artikel fehlt oder Bestand reicht nicht aus.', 1;

    INSERT INTO #TransDemoBestellungen (ArtikelID, Menge)
    VALUES (456, 2);

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0
        ROLLBACK TRANSACTION;

    THROW;
END CATCH;

SELECT ArtikelID, Bestand FROM #TransDemoLager;
SELECT BestellungID, ArtikelID, Menge
FROM #TransDemoBestellungen;

Der Bestand wird nur reduziert, wenn mindestens zwei Einheiten vorhanden sind. @@ROWCOUNT muss direkt nach dem UPDATE geprüft werden, weil spätere Anweisungen den Wert ändern können. Ist kein passender Artikel vorhanden oder reicht der Bestand nicht aus, löst THROW einen Fehler aus und der CATCH-Block rollt die Transaktion zurück. Auch ein Fehler beim anschließenden INSERT führt zum Rücksetzen beider Änderungen. Im Erfolgsfall steht der Bestand auf 8 und eine Bestellung ist erfasst.

In einer echten Anwendung kommen weitere Anforderungen hinzu: Kunden- und Artikelreferenzen, eindeutige Bestellnummern, gegebenenfalls mehrere Positionen und eine fachlich passende Behandlung von Wiederholungsversuchen. Das Beispiel konzentriert sich auf die Transaktionsgrenze.

Warum TRY…CATCH, XACT_ABORT und XACT_STATE zusammengehören

TRY...CATCH behandelt viele Laufzeitfehler, aber nicht jeden denkbaren Fehler. Beispielsweise können bestimmte Kompilierungsfehler auf derselben Ausführungsebene und Verbindungsabbrüche außerhalb des Blocks bleiben. Verlasse dich deshalb nicht darauf, dass jede Fehlerart in CATCH landet.

SET XACT_ABORT ON sorgt bei vielen Laufzeitfehlern dafür, dass eine laufende Transaktion vollständig abgebrochen wird. Microsoft weist darauf hin, dass THROW diese Einstellung berücksichtigt, RAISERROR dagegen nicht. XACT_STATE() zeigt im Fehlerblock, ob noch eine Transaktion existiert: 0 bedeutet keine aktive Transaktion, 1 eine bestätigungsfähige und -1 eine nicht mehr bestätigungsfähige Transaktion. Im Beispiel wird bei 1 oder -1 zurückgerollt und der ursprüngliche Fehler mit THROW; an die Anwendung weitergegeben.

Die Kombination folgt der Microsoft-Dokumentation zu TRY…CATCH und SET XACT_ABORT. Für produktive Prozeduren solltest du außerdem klären, ob sie selbst eine Transaktion eröffnen oder innerhalb einer bereits laufenden Transaktion aufgerufen werden. Ein unbedachtes ROLLBACK kann dann auch Änderungen des aufrufenden Codes zurücknehmen.

COMMIT, ROLLBACK und offene Transaktionen prüfen

COMMIT schließt eine erfolgreich ausgeführte Transaktion ab. ROLLBACK nimmt ihre bisherigen Änderungen zurück. Lässt du eine Transaktion offen, können Sperren länger bestehen bleiben, andere Sitzungen blockieren und die Freigabe des Transaktionsprotokolls behindern. Halte Transaktionen daher so kurz wie fachlich möglich und warte nicht innerhalb einer offenen Transaktion auf Benutzereingaben.

In der eigenen Sitzung kannst du den Zähler und den Zustand prüfen:

SELECT @@TRANCOUNT AS OffeneTransaktionsebenen,
       XACT_STATE() AS Transaktionszustand;

@@TRANCOUNT zählt BEGIN TRANSACTION-Ebenen, zeigt aber nicht an, ob die Transaktion noch bestätigt werden darf. Dafür ist XACT_STATE() gedacht. Wenn nach einem fehlgeschlagenen Versuch in deinem Abfragefenster unerwartet eine Transaktion offen ist, prüfe zuerst den Kontext, bevor du sie dort gezielt beendest.

Verschachtelte Transaktionen und SAVE TRANSACTION

Mehrere BEGIN TRANSACTION-Anweisungen erzeugen in SQL Server keine unabhängig bestätigbaren inneren Transaktionen. Ein inneres COMMIT verringert zunächst nur @@TRANCOUNT; erst das äußere COMMIT bestätigt die gesamte Arbeit. Ein gewöhnliches ROLLBACK setzt die gesamte Transaktion zurück.

Mit SAVE TRANSACTION lässt sich innerhalb einer bestätigungsfähigen Transaktion ein Rücksprungpunkt setzen. Ein ROLLBACK TRANSACTION SavepointName rollt dann Änderungen seit diesem Punkt zurück. Ein Savepoint ist aber kein eigener Commit und hilft nicht, wenn die Transaktion bereits nicht mehr bestätigungsfähig ist (XACT_STATE() = -1). Die Einzelheiten stehen in der Dokumentation zu SAVE TRANSACTION.

Welche Isolationsstufe gilt für Transaktionen in MSSQL?

Die Isolationsstufe bestimmt, welche Änderungen anderer Sitzungen eine Abfrage sehen kann und wie Lesezugriffe geschützt werden. Auf SQL Server ist READ COMMITTED die übliche Standardeinstellung. Je nach Datenbankoption READ_COMMITTED_SNAPSHOT nutzt sie Sperren oder Zeilenversionen. Prüfe deshalb die Konfiguration, bevor du Verhalten aus einem Beispiel auf eine andere Datenbank überträgst.

Isolationsstufe Typisches Verhalten Hinweis
READ UNCOMMITTED Kann nicht bestätigte Änderungen lesen Schmutzige Lesezugriffe sind möglich; kein allgemeiner Geschwindigkeitsknopf
READ COMMITTED Verhindert schmutzige Lesezugriffe Wiederholte Abfragen können unterschiedliche Ergebnisse liefern
REPEATABLE READ Schützt bereits gelesene Zeilen stärker Neue passende Zeilen können weiterhin erscheinen
SERIALIZABLE Schützt auch passende Schlüsselbereiche Kann stärker blockieren
SNAPSHOT Liest einen transaktionsweiten Stand über Zeilenversionen Muss für die Datenbank freigeschaltet sein; Schreibkonflikte sind möglich

READ_COMMITTED_SNAPSHOT ist keine zusätzliche SET TRANSACTION ISOLATION LEVEL-Stufe, sondern eine Datenbankoption, die das Verhalten von READ COMMITTED verändert. Die Optionen sollten nach Anforderungen und Messungen gewählt werden; eine Änderung betrifft mehr als eine einzelne Abfrage. Siehe die Microsoft-Referenz zu Isolationsstufen.

Blocking und Deadlocks in der Praxis

Zwei Transaktionen können sich gegenseitig blockieren, wenn sie dieselben Daten ändern wollen. Bei einem Deadlock beendet SQL Server eine der beteiligten Transaktionen als Opfer; die Anwendung erhält typischerweise Fehler 1205. Sie sollte dann – sofern fachlich sicher – die gesamte Arbeitseinheit erneut versuchen, nicht nur die zuletzt fehlgeschlagene Anweisung.

Kurze Transaktionen, passende Indizes und eine einheitliche Reihenfolge beim Zugriff auf Tabellen verringern viele Konflikte. Das gilt auch für das Bestellbeispiel: Bestand prüfen und ändern muss zusammen mit dem Speichern der Bestellung innerhalb derselben Transaktion geschehen. Microsofts Deadlock-Leitfaden beschreibt Diagnose und Gegenmaßnahmen.

Häufige Fragen zu Transaktionen in MSSQL

Muss ich für jede einzelne INSERT-Anweisung BEGIN TRANSACTION schreiben?

Nein. Einzelne Anweisungen laufen normalerweise im Autocommit-Modus. Eine ausdrückliche Transaktion brauchst du, wenn mehrere Schritte nur gemeinsam gelten sollen oder wenn du die Transaktionsgrenze bewusst steuern musst.

Rollt SQL Server bei jedem Fehler automatisch alles zurück?

Nein. Das hängt von Fehlerart, XACT_ABORT und dem Zustand der Transaktion ab. Behandle Fehler ausdrücklich und prüfe im CATCH-Block den Transaktionszustand. Ein SQL-Statement, das erfolgreich ausgeführt wird, aber null Zeilen ändert, ist zudem kein technischer Fehler.

Ersetzt eine Transaktion ein Backup?

Nein. Eine Transaktion steuert zusammengehörige Änderungen während ihrer Ausführung. Backups dienen der Wiederherstellung nach späteren Fehlern oder Datenverlust. Dazu findest du den Leitfaden zu Backup und Restore im SQL Server.

Fazit: Transaktionen in MSSQL zuverlässig einsetzen

Transaktionen in MSSQL schützen zusammengehörige Änderungen, wenn du die Grenze richtig setzt und auch fachliche Misserfolge erkennst. Prüfe nach kritischen UPDATE-Anweisungen die betroffenen Zeilen, rolle bei Fehlern kontrolliert zurück und gib Fehler an die Anwendung weiter. Halte die Transaktion kurz und wähle die Isolation passend zum gleichzeitigen Zugriff. So bleibt eine Bestellung nicht ohne Bestandsänderung stehen – und der Bestand sinkt nicht ohne Bestellung.