T-SQL-Transaktionen fassen mehrere Datenbankänderungen zu einer Einheit zusammen: Entweder werden alle Änderungen übernommen oder keine. Mit BEGIN TRANSACTION, COMMIT, ROLLBACK, TRY/CATCH und XACT_STATE() lässt sich dieses Verhalten gezielt steuern. Das folgende Beispiel zeigt, wie du eine Änderung bei einem Fehler zurückrollst und anschließend in einer Testdatenbank überprüfst.
Inhaltsverzeichnis
- Was ist eine Transaktion?
- BEGIN TRANSACTION, COMMIT und ROLLBACK
- Vollständiges Beispiel mit TRY/CATCH
- Was zeigt XACT_STATE() an?
- Typische Fehler vermeiden
- Transaktionen und MERGE
- Verhalten in einer Testdatenbank prüfen
Was ist eine Transaktion?
Eine Transaktion (transaction) ist eine zusammengehörige Folge von Datenbankoperationen. Ein typisches Beispiel ist eine Überweisung: Ein Betrag wird von einem Konto abgezogen und einem anderen gutgeschrieben. Nur wenn beide Änderungen erfolgreich sind, sollen sie dauerhaft gespeichert werden. Schlägt eine davon fehl, muss die Datenbank beide Änderungen verwerfen.
Transaktionen helfen, die Datenkonsistenz zu sichern. Sie sind besonders wichtig, wenn mehrere INSERT-, UPDATE-, DELETE– oder MERGE-Anweisungen fachlich zusammengehören.
BEGIN TRANSACTION, COMMIT und ROLLBACK
BEGIN TRANSACTIONstartet eine explizite Transaktion.COMMITbestätigt die Änderungen. Sie werden dauerhaft übernommen.ROLLBACKmacht Änderungen der laufenden Transaktion rückgängig.
Wird eine Transaktion weder committet noch zurückgerollt, bleibt sie offen. Das kann Sperren unnötig lange halten und weitere Zugriffe auf betroffene Daten verzögern.
Vollständiges Beispiel mit TRY/CATCH
Das folgende Beispiel erstellt eine kleine Demotabelle und legt ein Konto mit einem Anfangsguthaben an. Anschließend wird innerhalb einer Transaktion ein Betrag addiert. Ein absichtlich ausgelöster Fehler simuliert einen fehlgeschlagenen Arbeitsschritt. Der CATCH-Block prüft den Transaktionszustand, rollt bei Bedarf zurück und wirft den Fehler erneut aus.
Wichtig: Führe das Beispiel nur in einer geeigneten Testdatenbank und in einer Sitzung ohne bereits offene Transaktion aus. Die Tabelle wird nicht gelöscht; vorhandene Testdaten bleiben erhalten.
-- Einmalige Vorbereitung in einer Testdatenbank
IF OBJECT_ID(N'dbo.TransactionDemoAccount', N'U') IS NULL
BEGIN
CREATE TABLE dbo.TransactionDemoAccount
(
AccountId int NOT NULL PRIMARY KEY,
Balance decimal(12, 2) NOT NULL
);
END;
IF NOT EXISTS
(
SELECT 1
FROM dbo.TransactionDemoAccount
WHERE AccountId = 1
)
BEGIN
INSERT INTO dbo.TransactionDemoAccount (AccountId, Balance)
VALUES (1, 100.00);
END;
-- Vorherigen Kontostand notieren
SELECT AccountId, Balance
FROM dbo.TransactionDemoAccount
WHERE AccountId = 1;
Führe nun den folgenden Block aus. Der Befehl THROW löst absichtlich einen Fehler aus, damit du den Fehlerpfad testen kannst.
SET XACT_ABORT ON;
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
UPDATE dbo.TransactionDemoAccount
SET Balance = Balance + 50.00
WHERE AccountId = 1;
-- Simuliert einen Fehler nach der Datenänderung
;THROW 50001, N'Demonstrationsfehler: Änderung zurückrollen.', 1;
-- Wird im Fehlerfall nicht erreicht
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
DECLARE @StateBeforeRollback int = XACT_STATE();
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage,
@StateBeforeRollback AS XactStateBeforeRollback;
IF @StateBeforeRollback != 0
ROLLBACK TRANSACTION;
-- Fehler an den Aufrufer weitergeben
THROW;
END CATCH;
Der Fehler ist hier absichtlich eingebaut. Im CATCH-Block wird die Transaktion zurückgerollt und der ursprüngliche Fehler mit THROW erneut ausgelöst. Das letzte THROW sorgt dafür, dass der Fehler nicht stillschweigend verschluckt wird.
Um stattdessen den Erfolgsfall zu testen, entferne die Zeile mit dem absichtlichen THROW 50001. Dann wird COMMIT TRANSACTION erreicht und das Guthaben um 50 erhöht. Teste Rollback und Commit getrennt, damit du die Ergebnisse eindeutig zuordnen kannst.
Was zeigt XACT_STATE() an?
XACT_STATE() gibt an, ob eine Transaktion aktiv ist und ob SQL Server sie noch committet werden kann:
1: Eine aktive Transaktion kann Änderungen übernehmen oder zurückrollen.-1: Eine aktive Transaktion ist nicht mehr committbar. Es ist nur noch ein vollständiger Rollback möglich.0: Es ist keine aktive Transaktion vorhanden.
Im Beispiel wird der Wert vor dem Rollback gespeichert und ausgegeben. So kannst du sehen, in welchem Zustand die Transaktion beim Eintritt in den CATCH-Block ist. Anschließend wird zurückgerollt, wenn noch eine Transaktion aktiv ist.
XACT_STATE() und @@TRANCOUNT beantworten unterschiedliche Fragen: @@TRANCOUNT zählt die Transaktionsebenen. XACT_STATE() zeigt zusätzlich, ob die aktuelle Transaktion noch committbar ist. Für die Entscheidung, ob im Fehlerpfad ein Rollback nötig ist, ist XACT_STATE() daher besonders hilfreich.
Typische Fehler vermeiden
Offene Transaktionen
Ein häufiger Fehler ist ein BEGIN TRANSACTION ohne passenden COMMIT– oder ROLLBACK-Befehl. Die Verbindung kann dann Sperren weiter halten, obwohl die Anwendung schon mit einer anderen Aufgabe beschäftigt ist. Kontrolliere bei Problemen in der jeweiligen Sitzung den Wert von @@TRANCOUNT und sorge in jedem Fehlerpfad für einen Abschluss der Transaktion.
Fehlendes SET XACT_ABORT ON
Mit SET XACT_ABORT ON werden viele Laufzeitfehler so behandelt, dass SQL Server die laufende Transaktion beendet oder sie nicht mehr committbar macht. Ohne diese Einstellung kann das Verhalten je nach Fehler unterschiedlich sein: Eine Anweisung kann fehlschlagen, während die Transaktion weiter aktiv bleibt. Wer anschließend trotzdem committet oder den Fehler ignoriert, riskiert unerwartete Ergebnisse.
SET XACT_ABORT ON ersetzt TRY/CATCH nicht. Verwende beides zusammen: XACT_ABORT für das Verhalten bei Laufzeitfehlern und TRY/CATCH, um den Fehler zu behandeln, den Transaktionszustand zu prüfen und ihn nachvollziehbar weiterzugeben.
Im CATCH-Block immer COMMIT ausführen
Ein Fehlerpfad sollte eine fehlgeschlagene Transaktion nicht bestätigen. Prüfe stattdessen mit XACT_STATE(), ob noch eine Transaktion aktiv ist, und rolle sie zurück. Bei XACT_STATE() = -1 ist ein Commit nicht mehr möglich.
Fehler verschlucken
Wenn ein CATCH-Block den Fehler weder erneut auslöst noch anderweitig meldet, kann die aufrufende Anwendung den Fehlschlag übersehen. Das abschließende THROW im Beispiel gibt den Fehler an den Aufrufer weiter. In T-SQL ist THROW außerdem gegenüber RAISERROR vorzuziehen, wenn das Verhalten von SET XACT_ABORT berücksichtigt werden soll.
Transaktion in einer gespeicherten Prozedur verschachteln
Das Beispiel geht davon aus, dass es die Transaktion selbst startet. In SQL Server sind verschachtelte Transaktionen keine voneinander unabhängigen Transaktionen: Ein vollständiges ROLLBACK kann auch Änderungen einer äußeren Transaktion zurückrollen. Wenn eine Prozedur möglicherweise innerhalb einer bereits geöffneten Transaktion aufgerufen wird, muss die Transaktionsverwaltung darauf ausgelegt sein – beispielsweise mit einem Savepoint oder einer klar festgelegten Zuständigkeit für den Transaktionsabschluss.
Transaktionen und MERGE
MERGE kann Datensätze abhängig davon aktualisieren, ob passende Zeilen vorhanden sind, oder neue Zeilen einfügen. Die Anweisung ist selbst eine Datenänderung, ersetzt aber nicht die Transaktions- und Fehlerbehandlung. Wenn ein MERGE mit weiteren Änderungen fachlich eine Einheit bildet, kannst du ihn in dieselbe Transaktion aufnehmen:
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
-- MERGE auf die eigenen Tabellen und Schlüssel anpassen
MERGE dbo.Produkt AS Ziel
USING dbo.ProduktImport AS Quelle
ON Ziel.ProduktId = Quelle.ProduktId
WHEN MATCHED THEN
UPDATE SET Ziel.Bezeichnung = Quelle.Bezeichnung
WHEN NOT MATCHED BY TARGET THEN
INSERT (ProduktId, Bezeichnung)
VALUES (Quelle.ProduktId, Quelle.Bezeichnung);
-- Weitere zusammengehörige Datenänderungen hier ausführen
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF XACT_STATE() != 0
ROLLBACK TRANSACTION;
THROW;
END CATCH;
Die Tabellen und Spalten im Beispiel musst du an dein Schema anpassen. Ein Transaktionsblock macht MERGE nicht automatisch gegen alle Konkurrenzsituationen sicher. Teste die Anweisung deshalb mit realistischen Daten und gleichzeitigen Zugriffen. Weitere Hintergründe und Beispiele findest du im IT-Fundus-Artikel MERGE-Befehl in T-SQL: Die Anleitung für effiziente Datenaktualisierung.
Verhalten in einer Testdatenbank prüfen
So kannst du nachvollziehen, ob die Änderungen wirklich zurückgerollt wurden:
- Führe die Vorbereitung in einer Testdatenbank aus und notiere den angezeigten Kontostand.
- Starte den Fehlerfall mit dem absichtlichen
THROW. Im Ergebnis des Fehlerblocks siehst du die Fehlerinformationen undXACT_STATE(). - Führe danach in einem separaten Befehl dieselbe Abfrage für den Kontostand aus:
SELECT AccountId, Balance
FROM dbo.TransactionDemoAccount
WHERE AccountId = 1;
Der Kontostand sollte nach dem Fehlerfall dem vorher notierten Wert entsprechen. Im Erfolgsfall – wenn du den absichtlichen Fehler entfernst – sollte er um 50 erhöht sein. Prüfe zusätzlich, dass nach dem Fehler keine Transaktion offen ist, zum Beispiel mit:
SELECT @@TRANCOUNT AS OpenTransactionCount;
Führe solche Tests in einer eigenen Testdatenbank durch und nicht mit produktiven Tabellen. Das macht es einfacher, Fehlerfälle gezielt auszulösen und die erwarteten Ergebnisse zu kontrollieren.
Fazit
BEGIN TRANSACTION startet eine Transaktion, COMMIT übernimmt ihre Änderungen und ROLLBACK macht sie rückgängig. Mit TRY/CATCH, SET XACT_ABORT ON und XACT_STATE() lässt sich ein Fehlerpfad so gestalten, dass eine aktive Transaktion zurückgerollt und der Fehler weitergegeben wird. Das gilt auch, wenn Daten mit MERGE geändert werden: Entscheidend ist, dass zusammengehörige Änderungen eine klar definierte Transaktionsgrenze und eine vollständige Fehlerbehandlung haben.
Wenn du die Grundlagen von SQL auffrischen möchtest, lies auch den IT-Fundus-Beitrag Eine Einführung in die Datenbanksprache SQL.