2009-05-19 18 views
11

Sto cercando di mettere una dichiarazione try-catch in un trigger utilizzando Microsoft Server 2005.TSQL: try-catch transazione in trigger

BEGIN TRANSACTION 
BEGIN TRY 
    --Some More SQL 
    COMMIT TRANSACTION 
END TRY 
BEGIN CATCH 
    IF (XACT_STATE()) = -1 
    BEGIN 
     ROLLBACK TRANSACTION; 
    END; 
END CATCH 

Il problema è che io non voglio il grilletto per fallire se qualcosa viene catturato dal blocco try-catch. Al momento, ricevo l'errore "La transazione è terminata nel trigger. Il batch è stato interrotto". se la transazione fallisce. Come posso far fallire il grilletto con grazia?


Inoltre, se tolgo la transazione, ottengo l ' "Operazione destinata a grilletto. Batch è stata interrotta." Errore.

BEGIN TRY 
    --Some More SQL 
END TRY 
BEGIN CATCH 
    return 
END CATCH 

C'è un modo per aggirare questo?

+0

Sto cercando di aggiornare un database legacy ogni volta che nulla è inserito in una tabella. Il problema è che non voglio che l'inserimento fallisca se il trigger fallisce. Il database precedente non è il sistema più affidabile. – Eldila

+13

Inizierò a usare la parola "condannato" in più dei miei messaggi di errore. – AaronLS

risposta

7

Non eseguire il rollback in un trigger e non è necessario avviare una transazione.

Il ROLLBACK TRANSACTION eseguirà il rollback del trigger DML originale e anche la transazione di trigger aggiuntiva. Così il lotto verrà interrotto

Edit:

Suggerisco di non avere un "ritorno" in blocco catch e semplicemente permettere al codice per completare non ho mai ignorato un errore di intrappolato in un trigger (ma io usare TRY/CATCH nei trigger con rollback e raiserror da rilanciare quindi questa è un'ipotesi, ma il ritorno è probabilmente una condizione di uscita anomala nel trigger

Inoltre, cercare di evitare la condizione di errore in primo luogo . Modificare --some more sql per evitare l'errore. Esempio, aggiungi if exists(... per verificare un duplicato prima o simile

+0

Questo è veramente utile. Tuttavia, ho appena incontrato un altro problema. Si prega di consultare la domanda aggiornata per i dettagli. – Eldila

+1

Sfortunatamente, senza un 'ROLLBACK' in un trigger - che è molto disordinato e può rovinare completamente il supporto delle transazioni .NET! - Non conosco un modo per * applicare assolutamente i vincoli * con i trigger. 'RAISERROR (..., 16,1)' è sufficiente per far notare .NET e lanciare un'eccezione in molti casi, ma le transazioni "autocommit" (IMPLICIT_TRANSACTIONS = OFF) sono * non influenzate * da 'RAISERROR' e quindi bypassano qualsiasi vincolo che questo trigger stava cercando di aggiungere. L'unica "soluzione" che ho trovato è quella di * non * utilizzare le transazioni "autocommit" e esplicitamente ROLLBACK/COMMIT con TRY/CATCH. Peggio. Design. Mai. –

+0

(Ovviamente Sybase ha un 'ROLLBACK TRIGGER' perfettamente valido: - /) –

8

Nella mia esperienza, qualsiasi errore rilevato in un tentativo di cattura in un trigger arresta l'intera transazione; potresti essere in grado di utilizzare una transazione di salvataggio. Penso che tu debba guardare a cosa sta succedendo in "Some more sql" e determinare se puoi scrivere case/se le dichiarazioni intorno a esso per fermare l'errore.

Cosa si può essere in todo in grado a seconda di quello che stai facendo è usare un save transaction e catturare che il fermo

Nel codice qualcosa di simile

SAVE TRANSACTION BeforeUpdate; 
BEGIN TRY 
     --Some More SQL 
END TRY 
BEGIN CATCH 
ROLLBACK TRANSACTION BeforeUpdate; 
     return 
END CATCH 
+0

Non sapevo di SAVE TRANSACTION +1 – ichiban

+0

E 'passato un po' di tempo :) ha un impatto sulle prestazioni, quindi usa con parsimonia. – u07ch

+1

Significa che dobbiamo avere una transazione aperta? Possiamo aprirlo all'inizio del trigger? – meir

1

u07ch,

Purtroppo non è possibile utilizzare la transazione di salvataggio e provare .. prendere insieme - semplicemente non possono lavorare insieme:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/11/15/avoid-mixing-old-and-new-styles-of-error-handling.aspx

+1

Può essere fatto; [ecco un esempio di Microsoft] (http://msdn.microsoft.com/en-us/library/ms188378.aspx). È solo un PITA. Devi sapere se hai creato la transazione (e come) e poi hai bisogno di guardare XACT_STATE alla pulizia ... e questo non è nemmeno nel * contesto di un trigger * che è completamente diverso da brutto. Odio le transazioni di SQL Server e "gestione delle eccezioni". –

+0

@pst a volte non può essere fatto: se la tua transazione è condannata, non è possibile tornare al tuo punto di salvataggio. Tutto quello che puoi fare è eseguire il rollback dell'intera transazione. –

+0

Naturalmente ... l'esempio copre anche questo caso. –

0

Potrebbe essere utile sapere cosa stai cercando di fare nel trigger.

Il trigger è parte della transazione che ha inviato i dati alle tabelle inserite o eliminate. Se fallisce, eseguirà il rollback dell'intera transazione. Se ti aspetti che il trigger fallisca di tanto in tanto ma non esegui il rollback della dichiarazione che ha innescato il trigger, allora forse hai bisogno di ripensare se un trigger è la cosa giusta da usare.

+0

Cosa usare invece di? – Joel

2

Per evitare di perdere i dati transazionali prima dell'azione di attivazione, è necessario chiamare COMMIT TRAN. Fallo prima del blocco TRY/CATCH e otterrai i risultati desiderati.

Esempio:

COMMIT TRAN 
BEGIN TRY 
    -- possible error occurs here... 
END TRY 
BEGIN CATCH 
    PRINT 'Error on line ' + CAST(ERROR_LINE() AS VARCHAR(10)) 
    PRINT ERROR_MESSAGE() 
END CATCH 

getterà il seguente errore ancora - non è sicuro come evitare:

The transaction ended in the trigger. The batch has been aborted. 

Ma sia la transazione originale e la transazione grilletto dovrebbero impegnarsi con successo.

AGGIORNAMENTO: Per evitare l'ultimo errore dell'eccezione, chiamare BEGIN TRAN nell'istruzione TRY. Nota, Microsoft consiglia di NON chiamare COMMIT TRAN all'interno di un trigger, ma se inevitabile, questo dovrebbe funzionare per te.

Esempio:

COMMIT TRAN 
BEGIN TRY 
    BEGIN TRAN 
0

È possibile impostare XACT_ABORT OFF all'inizio del grilletto.

1

Questa demo raggiunge molte delle domande sopra riportate. I messaggi di errore diventano facoltativi. Il trucco che lo fa funzionare è nell'esecuzione dinamica nidificata.

if object_id('toto') is not null drop table toto 
    go 
    create table toto (i int); 
    go 
    if object_id('toto2') is not null drop table toto2 
    go 
    create table toto2 (i int); 
    go 
    create Trigger trtoto 
    ON toto 
    Instead Of Insert 
    as 
    Begin 
     BEGIN TRY 
     set nocount on 
     insert into toto values(2) 

     declare @sql nvarchar(max) = 'insert into toto2 values(3); select * from ThisTableDoesntexist' 

     Exec sp_executeSql N'set xact_abort off; exec (@sql) ', N'@sql nvarchar(max)', @sql 

     END TRY 

     BEGIN CATCH 
     PRINT 'Error on line ' + CAST(ERROR_LINE() AS VARCHAR(10)) 
     PRINT ERROR_MESSAGE() 
     END CATCH 
    End 

GO 
-- tests 
set nocount on 
insert into toto values (1) -- is not inserted on purpose by the trigger 
select * from toto -- other value inserted despite the error 
select * from toto2 -- other value inserted in other table despite the error 
1

Utilizzare SET XACT_ABORT OFF .Quando errore Transact-SQL dichiarazione incontro, appena sollevato il messaggio di errore e l'operazione continua l'elaborazione. Il seguente codice è quello di creare il grilletto:

Create TRIGGER [dbo].tr_Ins_Table_Master ON [dbo].Table_Master 
AFTER INSERT 
AS 
BEGIN 
set xact_abort off 
BEGIN TRY 
     --your SQL   
     INSERT INTO Table_Detail 
     SELECT MasterID,Name FROM INSERTED 

END TRY 

BEGIN CATCH  
    select ERROR_MESSAGE() 
END CATCH 

END