Was ist der Vorteil SET XACT_ABORT ON
einer gespeicherten Prozedur?
Was ist der Vorteil SET XACT_ABORT ON
einer gespeicherten Prozedur?
Antworten:
SET XACT_ABORT ON
Weist SQL Server an, die gesamte Transaktion zurückzusetzen und den Stapel abzubrechen, wenn ein Laufzeitfehler auftritt. Es behandelt Sie in Fällen wie einem Befehlszeitlimit, das in der Clientanwendung und nicht in SQL Server selbst auftritt (was nicht durch die Standardeinstellung abgedeckt ist XACT_ABORT OFF
).
Da ein Abfragezeitlimit die Transaktion offen lässt, SET XACT_ABORT ON
wird dies in allen gespeicherten Prozeduren mit expliziten Transaktionen empfohlen (sofern Sie keinen bestimmten Grund haben, etwas anderes zu tun), da die Folgen einer Anwendung, die Arbeiten an einer Verbindung mit einer offenen Transaktion ausführt, katastrophal sind.
Es gibt einen wirklich guten Überblick auf Dan Guzmans Blog ,
BEGIN TRY
- BEGIN CATCH
und ROLLBACK
mit dem BEGIN CATCH
Block in SQL haben?
BEGIN TRY
- Fängt BEGIN CATCH
keine Zeitüberschreitungen in der Clientanwendung ab, und einige SQL-Fehler sind auch nicht abfangbar, sodass Sie eine offene Transaktion haben, bei der Sie keine erwarten würden.
Meiner Meinung nach wurde SET XACT_ABORT ON durch das Hinzufügen von BEGIN TRY / BEGIN CATCH in SQL 2k5 überholt. Vor Ausnahmeblöcken in Transact-SQL war es wirklich schwierig, Fehler zu behandeln, und unausgeglichene Prozeduren waren allzu häufig (Prozeduren, die beim Beenden ein anderes @@ TRANCOUNT hatten als beim Eintritt).
Mit der Hinzufügung von Transact-SQL ist die Ausnahmebehandlung viel einfacher, korrekte Prozeduren zu schreiben, die garantiert einen ordnungsgemäßen Ausgleich der Transaktionen gewährleisten. Zum Beispiel verwende ich diese Vorlage für die Ausnahmebehandlung und verschachtelte Transaktionen :
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare @trancount int;
set @trancount = @@trancount;
begin try
if @trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if @trancount = 0
commit;
end try
begin catch
declare @error int, @message varchar(4000), @xstate int;
select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
if @xstate = -1
rollback;
if @xstate = 1 and @trancount = 0
rollback
if @xstate = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
end catch
end
go
Es ermöglicht mir, atomare Prozeduren zu schreiben, die bei behebbaren Fehlern nur ihre eigene Arbeit zurücksetzen.
Eines der Hauptprobleme bei Transact-SQL-Prozeduren ist die Datenreinheit : Manchmal sind die empfangenen Parameter oder die Daten in den Tabellen einfach falsch, was zu doppelten Schlüsselfehlern, referenziellen Einschränkungsfehlern, Überprüfungsbeschränkungsfehlern usw. führt. Schließlich ist dies genau die Rolle dieser Einschränkungen. Wenn diese Datenreinheitsfehler unmöglich wären und alle von der Geschäftslogik erfasst würden, wären die Einschränkungen veraltet (dramatische Übertreibung für den Effekt hinzugefügt). Wenn XACT_ABORT aktiviert ist, gehen all diese Fehler dazu, dass die gesamte Transaktion verloren geht, anstatt Ausnahmeblöcke codieren zu können, die die Ausnahme ordnungsgemäß behandeln. Ein typisches Beispiel ist der Versuch, ein INSERT durchzuführen und bei einer PK-Verletzung zu einem UPDATE zurückzukehren.
MSDN zitieren :
Wenn SET XACT_ABORT aktiviert ist und eine Transact-SQL-Anweisung einen Laufzeitfehler auslöst, wird die gesamte Transaktion beendet und zurückgesetzt. Wenn SET XACT_ABORT auf OFF gesetzt ist, wird in einigen Fällen nur die Transact-SQL-Anweisung, die den Fehler ausgelöst hat, zurückgesetzt und die Transaktion wird weiter verarbeitet.
In der Praxis bedeutet dies, dass einige der Anweisungen möglicherweise fehlschlagen und die Transaktion "teilweise abgeschlossen" bleibt. Für einen Anrufer gibt es möglicherweise keine Anzeichen für diesen Fehler.
Ein einfaches Beispiel:
INSERT INTO t1 VALUES (1/0)
INSERT INTO t2 VALUES (1/1)
SELECT 'Everything is fine'
Dieser Code würde 'erfolgreich' mit XACT_ABORT OFF ausgeführt und mit einem Fehler mit XACT_ABORT ON beendet ('INSERT INTO t2' wird nicht ausgeführt und eine Clientanwendung löst eine Ausnahme aus).
Als flexibleren Ansatz können Sie nach jeder Anweisung (alte Schule) @@ ERROR überprüfen oder TRY ... CATCH-Blöcke (MSSQL2005 +) verwenden. Persönlich ziehe ich es vor, XACT_ABORT auf ON zu setzen, wenn es keinen Grund für eine erweiterte Fehlerbehandlung gibt.
In Bezug auf Client-Timeouts und die Verwendung von XACT_ABORT, um sie zu verarbeiten, gibt es meiner Meinung nach mindestens einen sehr guten Grund, Timeouts in Client-APIs wie SqlClient zu verwenden, nämlich den Client-Anwendungscode vor Deadlocks zu schützen, die im SQL Server-Code auftreten. In diesem Fall ist der Client-Code fehlerfrei, muss sich jedoch selbst davor schützen, für immer zu blockieren, bis der Befehl auf dem Server ausgeführt wird. Wenn umgekehrt Client-Timeouts zum Schutz des Client-Codes vorhanden sein müssen, muss XACT_ABORT ON den Server-Code vor Client-Abbrüchen schützen, falls die Ausführung des Server-Codes länger dauert, als der Client warten möchte.