Qual è il vantaggio dell'utilizzo SET XACT_ABORT ON
in una procedura memorizzata?
Qual è il vantaggio dell'utilizzo SET XACT_ABORT ON
in una procedura memorizzata?
Risposte:
SET XACT_ABORT ON
indica a SQL Server di eseguire il rollback dell'intera transazione e di interrompere il batch quando si verifica un errore di runtime. Ti copre in casi come un timeout del comando che si verifica sull'applicazione client piuttosto che all'interno dello stesso SQL Server (che non è coperto dall'impostazione predefinita XACT_ABORT OFF
).
Poiché un timeout della query lascerà aperta la transazione, SET XACT_ABORT ON
è consigliabile in tutte le procedure memorizzate con transazioni esplicite (a meno che non si abbia un motivo specifico per fare diversamente) poiché le conseguenze di un'applicazione che esegue un lavoro su una connessione con una transazione aperta sono disastrose.
C'è una fantastica panoramica sul blog di Dan Guzman ,
BEGIN TRY
- BEGIN CATCH
e ROLLBACK
con il BEGIN CATCH
blocco in Sql?
BEGIN TRY
- BEGIN CATCH
non rileverà cose come un timeout che si verifica nell'applicazione client e anche alcuni errori SQL sono irraggiungibili, lasciandoti con una transazione aperta dove non te lo aspetti.
A mio avviso, SET XACT_ABORT ON è stato reso obsoleto dall'aggiunta di BEGIN TRY / BEGIN CATCH in SQL 2k5. Prima dei blocchi delle eccezioni in Transact-SQL era davvero difficile gestire gli errori e le procedure sbilanciate erano fin troppo comuni (procedure che avevano un @@ TRANCOUNT diverso all'uscita rispetto all'entrata).
Con l'aggiunta della gestione delle eccezioni Transact-SQL è molto più semplice scrivere le procedure corrette che garantiscono il corretto bilanciamento delle transazioni. Ad esempio, utilizzo questo modello per la gestione delle eccezioni e le transazioni nidificate :
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
Mi permette di scrivere procedure atomiche che eseguano il rollback solo del proprio lavoro in caso di errori recuperabili.
Uno dei problemi principali affrontati dalle procedure Transact-SQL è la purezza dei dati : a volte i parametri ricevuti o i dati nelle tabelle sono semplicemente errati, con conseguenti errori chiave duplicati, errori di vincolo referenziale, errori di vincolo di controllo e così via e così via. Dopotutto, questo è esattamente il ruolo di questi vincoli, se questi errori di purezza dei dati sarebbero impossibili e tutti catturati dalla logica aziendale, i vincoli sarebbero tutti obsoleti (drammatica esagerazione aggiunta per effetto). Se XACT_ABORT è ON, tutti questi errori comportano la perdita dell'intera transazione, anziché essere in grado di codificare i blocchi di eccezioni che gestiscono l'eccezione con grazia. Un esempio tipico è il tentativo di eseguire un INSERT e il ripristino di un UPDATE su violazione PK.
Citando MSDN :
Quando SET XACT_ABORT è ON, se un'istruzione Transact-SQL genera un errore di runtime, l'intera transazione viene terminata e ripristinata. Quando SET XACT_ABORT è OFF, in alcuni casi viene eseguito il rollback solo dell'istruzione Transact-SQL che ha generato l'errore e la transazione continua l'elaborazione.
In pratica ciò significa che alcune delle dichiarazioni potrebbero non riuscire, lasciando la transazione "parzialmente completata" e potrebbe non esserci alcun segno di questo errore per un chiamante.
Un semplice esempio:
INSERT INTO t1 VALUES (1/0)
INSERT INTO t2 VALUES (1/1)
SELECT 'Everything is fine'
Questo codice verrebbe eseguito "correttamente" con XACT_ABORT OFF e terminerà con un errore con XACT_ABORT ON ("INSERT INTO t2" non verrà eseguito e un'applicazione client genererà un'eccezione).
Come approccio più flessibile, è possibile controllare @@ ERROR dopo ogni istruzione (vecchia scuola) o utilizzare i blocchi TRY ... CATCH (MSSQL2005 +). Personalmente preferisco impostare XACT_ABORT ON ogni volta che non vi è alcun motivo per una gestione avanzata degli errori.
Per quanto riguarda i timeout dei client e l'uso di XACT_ABORT per gestirli, secondo me c'è almeno un ottimo motivo per avere timeout nelle API client come SqlClient, e cioè proteggere il codice dell'applicazione client dai deadlock che si verificano nel codice del server SQL. In questo caso il codice client non ha alcun difetto, ma deve proteggersi dal blocco per sempre in attesa del completamento del comando sul server. Quindi, al contrario, se devono esistere timeout del client per proteggere il codice client, così XACT_ABORT ON deve proteggere il codice del server dalle interruzioni del client, nel caso in cui il codice del server impieghi più tempo per l'esecuzione di quanto il client è disposto ad aspettare.