2010-12-10 13 views
6

Ho trovato e articolo nel MSDN Lbrary spiegando che try/catch non gestisce gli errori generati quando un oggetto non può essere trovato. Così, anche se mi avvolgo una transazione in un try/catch, la frase rollback viene eseguito:gestione errori di transazione quando gli oggetti non esistono

BEGIN TRY 
BEGIN TRANSACTION 

    SELECT 1 FROM dbo.TableDoesNotExists 
    PRINT ' Should not see this' 
    COMMIT TRANSACTION 
END TRY 
BEGIN CATCH 
    ROLLBACK TRANSACTION 
    SELECT 
      ERROR_MESSAGE() 
END CATCH 

--PRINT 'Error Number before go: ' + CAST(@@Error AS VARCHAR) 

go 
PRINT 'Error Count After go: ' + CAST(@@Error AS VARCHAR) 
PRINT 'Transaction Count ' + CAST(@@TRANCOUNT AS VARCHAR) 

qual è il modo consigliato per gestire gli errori generata quando un oggetto non esiste, soprattutto quando v'è una transazione coinvolti . Devo aggiungere questo bit di codice al posto delle ultime due istruzioni di stampa:

IF @@ERROR <> 0 AND @@TRANCOUNT > 0 
BEGIN 
    PRINT 'Rolling back txn' 
    ROLLBACK TRANSACTION 
END 

go 

PRINT 'Transaction Count again: ' + CAST(@@TRANCOUNT AS VARCHAR) 

risposta

0

Perché stai cercando di recuperare i dati da una tabella che non esiste?

La componente fondamentale di un database è una tabella. Non sapere cosa c'è nel tuo schema sta essenzialmente cercando di usare SQL come linguaggio dinamico, cosa che non lo è.

Vorrei ripensare il tuo design; senza saperne di più sulle tabelle nel database e il suo uso previsto è difficile per gli altri aiutare in questo senso. Aggiungi qualche altra informazione alla tua domanda per favore.

EDIT ho avuto una lettura di Bol e il modo consigliato per gestire gli errori quando un oggetto non esiste è la seguente:

È possibile utilizzare try ... catch per gestire gli errori che si verificano durante la compilazione o la ricompilazione a livello di istruzione da eseguendo il codice di generazione degli errori in un batch separato all'interno del blocco TRY. Ad esempio, lo si fa posizionando il codice in una stored procedure o tramite eseguendo un'istruzione dinamica Transact-SQL utilizzando sp_executesql. Questo consente a TRY ... CATCH di rilevare l'errore a un livello superiore di esecuzione rispetto all'occorrenza dell'errore .

Ho testato questo utilizzando la seguente procedura

CREATE PROCEDURE [dbo].[BrokenProcedure] 
AS 
BEGIN 
    SET NOCOUNT ON; 
    SELECT * FROM MissingTable 
END 
GO 

poi chiamato entro un TRY..CATCH blocco:

BEGIN TRY 
    PRINT 'Error Number before: ' + CAST(@@Error AS VARCHAR) 
    EXECUTE [dbo].[BrokenProcedure] 
    PRINT ' Should not see this' 
END TRY 
BEGIN CATCH 
    PRINT 'Error Number in catch: ' + CAST(@@Error AS VARCHAR) 
END CATCH 

Conseguente il seguente output: Numero

errore prima: 0

Errore numero in cattura: 208

Non è una soluzione perfetta, come si dovrà creare procedure (o utilizzare SQL dinamico) per tutto il vostro accesso alle tabelle, ma è il metodo di raccomandare da MS.

+1

volte in ambienti dinamici, non si conosce lo schema preciso ; un altro processo potrebbe cambiare la struttura del database, quindi è essenziale che la normale gestione delle eccezioni try/catch funzioni normalmente. –

+0

Punto giusto. Sì, la gestione try/catch dovrebbe funzionare correttamente ma non difendevo MS; Semplicemente non sono d'accordo con questo uso di un database. – Tony

+0

Tony, Questo scenario si presenta nel nostro ambiente di test e sviluppo in cui diversi sviluppatori lavorano simultaneamente alle modifiche che stanno apportando. Ora questa è una situazione su cui non ho controllo, quindi non posso cambiarla. Tuttavia, poiché possono verificarsi modifiche agli oggetti nel test, devo essere in grado di gestire gli errori generati da oggetti mancanti e ho pensato che il meccanismo try/catch dovrebbe essere in grado di gestirli. Dal momento che non sto cercando di capire il modo migliore per utilizzare le transazioni in modo che le modifiche di cui sono responsabile non finiscano in una sorta di stato incoerente. – gr928x

0

Ora, hai riscontrato un problema interessante (beh, almeno per me). Non si dovrebbe iniziare una transazione perché il batch non verrà compilato. Tuttavia, può essere compilato e successivamente la ricompilazione a livello di istruzioni non riesce più tardi.

See this question What is wrong with my Try Catch in T-SQL?

Tuttavia, in entrambi i caso è possibile utilizzare SET XACT_ABORT ON. Ciò aggiunge prevedibilità perché un effetto consiste nel ripristinare automaticamente qualsiasi transazione. Si sopprime anche errore 266. Vedere questo SO question troppo

SET XACT_ABORT ON 
BEGIN TRY 
    BEGIN TRANSACTION 

    SELECT 1 FROM dbo.TableDoesNotExists 
    PRINT ' Should not see this' 
    COMMIT TRANSACTION 
END TRY 
BEGIN CATCH 
    -- not needed, but looks weird without a rollback. 
    -- you could forget SET XACT_ABORT ON 
    -- Use XACT_STATE avoid double rollback errors 
    IF XACT_STATE() <> 0 
     ROLLBACK TRANSACTION 

    SELECT 
      ERROR_MESSAGE() 
END CATCH 

go 
--note, this can't be guaranteed to give anything 
PRINT 'Error Count After go: ' + CAST(@@Error AS VARCHAR) 
PRINT 'Transaction Count ' + CAST(@@TRANCOUNT AS VARCHAR) 
1

È possibile verificare l'esistenza di un oggetto con OBJECT_ID():

IF OBJECT_ID('MyTable') IS NULL RAISERROR('Could not find MyTable.', 18, 0) 
+1

sì, potrei, ma questo non è scalabile. Immagina se stia sviluppando uno script di test che tocca 50 oggetti, eseguo il test per ognuno di essi? Mi sembra che try/catch dovrebbe gestire l'errore in primo luogo. – gr928x