Qual è il metodo migliore per aggiungere la gestione degli errori nei processi memorizzati di SQL 2005?


11

Qual è un buon modo per rendere i proc memorizzati abbastanza robusti da poter scalare molto bene e contenere anche la gestione degli errori?

Inoltre, qual è il modo migliore per gestire più scenari di errore in un proc memorizzato e avere un sistema di feedback intelligente che restituirà informazioni significative sull'errore alle app chiamanti?


2
Prova a utilizzare il nuovo blocco TRY CATCH in SQL Server 2005. sommarskog.se/error_handling_2005.html
Sankar Reddy,

Ciao @Kacalapy ~ Vorrei raccomandare in futuro di porre ogni domanda a sé stante e in questo modo possiamo avere risposte specifiche focalizzate su una domanda alla volta. Ti incoraggio a farlo con questa domanda.
jcolebrand

Risposte:


12

Alex Kuznetsov ha un grande capitolo nel suo libro Defensive Database Programming (Capitolo 8) che tratta T-SQL TRY ... CATCH, le transazioni T-SQL e le impostazioni SET XACT_ABORT e l'utilizzo della gestione degli errori sul lato client. Ti aiuterà molto a decidere quale delle opzioni ha più senso per ciò che devi realizzare.

È disponibile gratuitamente su questo sito . Non sono in alcun modo affiliato con la compagnia, ma possiedo la versione cartacea di quel libro.

Ci sono molti piccoli dettagli su questo argomento che sono spiegati molto bene da Alex.

Per richiesta di Nick ... (ma non tutto questo è nel capitolo)

In termini di ridimensionamento, devi essere brutalmente onesto su quali attività devono essere nel codice db e quali dovrebbero essere nell'app. Hai mai notato quanto il codice ad esecuzione rapida tende a tornare alla progettazione per una singola preoccupazione per metodo?

Il modo più semplice per comunicare sarebbe codici di errore personalizzati (> 50.000). È anche abbastanza veloce. Significa che dovresti mantenere sincronizzati il ​​codice db e il codice dell'app. Con un codice di errore personalizzato, è anche possibile restituire informazioni utili nella stringa del messaggio di errore. Poiché hai un codice di errore strettamente per quella situazione, puoi scrivere un parser nel codice dell'app su misura per il formato dei dati dell'errore.

Inoltre, quali condizioni di errore richiedono un nuovo tentativo di logica nel database? Se vuoi riprovare dopo X secondi, è meglio gestirlo nel codice dell'app in modo che la transazione non si blocchi tanto. Se stai solo reinviando subito un'operazione DML, ripeterla in SP potrebbe essere più efficiente. Tieni presente, tuttavia, che dovrai eventualmente duplicare il codice o aggiungere un livello di SP per eseguire un nuovo tentativo.

Davvero, questo è attualmente il più grande problema con la logica TRY ... CATCH in SQL Server al momento. Può essere fatto, ma è un po 'un gufo. Cercare alcuni miglioramenti a questo proposito in SQL Server 2012, in particolare rilanciando le eccezioni del sistema (preservando il numero di errore originale). Inoltre, c'è FORMATMESSAGE , che aggiunge una certa flessibilità nella costruzione di messaggi di errore, in particolare ai fini della registrazione.


Ottimo consiglio e un ottimo libro!
Marian,

Red Gate offre molti e-book gratuiti estremamente utili, e questo è sicuramente uno dei migliori. Ottimo consiglio
Matt M

Non tutti i loro libri lo fanno, ma la versione gratuita del libro "Difensiva ..." di Kuznetsov non contiene gli ultimi 2 capitoli sui livelli di isolamento delle transazioni e sulle modifiche di sviluppo che sopravvivono alla concorrenza. Per me. il contenuto è valso l'acquisto.
Phil Helmer,

7

Questo è il nostro modello (registrazione degli errori rimossa)

Appunti:

  • Senza XACT_ABORT, tutti gli TXN iniziano e il commit / rollback deve essere associato
  • Un commit diminuisce @@ TRANCOUNT
  • Un rollback restituisce @@ TRANCOUNT a zero in modo da ottenere l'errore 266
  • Non puoi ROLLBACK solo il layer corrente (es. Decremento @@ TRANCOUNT al rollback)
  • XACT_ABORT elimina l'errore 266
  • Ogni proc memorizzato deve essere conforme allo stesso modello, quindi ogni chiamata è atomica
  • Il controllo di rollback è effettivamente ridondante a causa di XACT_ABORT. Tuttavia, mi fa sentire meglio, sembra strano senza e consente situazioni in cui non lo desideri
  • Ciò consente TXN lato client (come LINQ)
  • Remus Rusanu ha una shell simile che utilizza i punti di salvataggio. Preferisco una chiamata atomica al DB e non uso aggiornamenti parziali come il loro articolo

... quindi non creare più TXN del necessario

Tuttavia,

CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON

DECLARE @starttrancount int

BEGIN TRY
    SELECT @starttrancount = @@TRANCOUNT

    IF @starttrancount = 0
        BEGIN TRANSACTION

       [...Perform work, call nested procedures...]

    IF @starttrancount = 0 
        COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
        ROLLBACK TRANSACTION
    RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO

cosa succede se @@ TRANCOUNT è maggiore di 0? non lavori o hai feedback?
kacalapy,

@kacalapy: non esiste una transazione nidificata, quindi non iniziamo un'altra scribd.com/doc/49579859/33/Nested-Transactions-Are-Real
gbn

3

Uso Try / Catch, ma raccolgo anche quante più informazioni possibili e le scrivo in un log degli errori DOPO il rollback. In questo esempio, "LogEvent" è una procedura memorizzata che scrive in una tabella EventLog, contenente i dettagli di ciò che è accaduto. GetErrorInfo () è una chiamata di funzione che restituisce il messaggio di errore esatto.

Quando si verifica un errore, le informazioni vengono raccolte, la procedura passa alla sezione di gestione degli errori e genera un rollback. Le informazioni vengono scritte nel registro, quindi la procedura viene chiusa.

Considerando le chiamate extra di procedura / funzione coinvolte, sembra un po 'esagerato. TUTTAVIA questo metodo è estremamente utile quando si tenta di eseguire il debug del problema.

exec LogEvent @Process, @Database, 'Tentativo di inserire blah blah blah'
INIZIA A PROVARE
  inserire in MyTable
  seleziona valori
    da MyOtherTable

  selezionare @rowcount = @@ ROWCOUNT
PROVA FINALE
- ErrorHandling
INIZIA CATTURA
  seleziona @error = ERROR_NUMBER (),
         @rowcount = -1,
         @TableAction = 'insert',
         @TableName = @Database + '.MyTable',
         @AdditionalInfo = '(Tentativo di inserire blah blah blah)' + dbo.GetErrorInfo ()
   GOTO TableAccessError
FINE CATTURA

.
.
.
.

TableAccessError:
ROLLBACK IF (@@ TRANCOUNT> 0)
seleziona @output = upper (@TableAction) + 
       'ERRORE - Si è verificato un errore durante' + 
       case (@TableAction)
         quando "aggiorna", quindi "aggiorna"
         quando "elimina", quindi "elimina"
         else @TableAction + 'ing'
       fine + 
       'records' + 
       caso (@TableAction) 
         quando 'seleziona' quindi 'da' 
         quando "aggiorna", quindi "in" 
         quando 'inserisci' quindi 'in'
         altro "da"   
         fine + 
         'the' + @TableName + 'table.'
seleziona @output = @output + '@@ ERROR:' + convert (varchar (8), @ error) 
seleziona @output = @output + '@@ ROWCOUNT:' + convert (varchar (8), @ rowcount) 

seleziona @output = @output + isnull (@AdditionalInfo, '')
exec LogEvent @Process, @Database, @Output
RAISERROR (@ output, 16,1) con log
seleziona @ReturnCode = -1
GOTO THE_EXIT


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.