Quelle est la meilleure méthode pour ajouter la gestion des erreurs dans les procs stockés SQL 2005?

11

Quel est un bon moyen de rendre les proc stockés suffisamment robustes pour qu'ils puissent très bien évoluer et contenir également la gestion des erreurs?

De plus, quelle est la meilleure façon de gérer plusieurs scénarios d'erreur dans un processus stocké et d'avoir un système de rétroaction intelligent qui retournera des informations d'erreur significatives aux applications appelantes?

kacalapy
la source
2
Essayez d'utiliser le nouveau bloc TRY CATCH dans SQL Server 2005. sommarskog.se/error_handling_2005.html
Sankar Reddy
Salut @Kacalapy ~ Je voudrais recommander à l'avenir de poser chaque question à part entière et de cette façon, nous pouvons avoir des réponses spécifiques centrées sur une question à la fois. Je vous encourage à le faire avec cette question.
jcolebrand

Réponses:

12

Alex Kuznetsov a un grand chapitre dans son livre Defensive Database Programming (Chapter 8) qui couvre T-SQL TRY ... CATCH, les transactions T-SQL & SET XACT_ABORT et l'utilisation de la gestion des erreurs côté client. Cela vous aidera beaucoup à décider laquelle des options est la plus logique pour ce que vous devez accomplir.

Il est disponible gratuitement sur ce site . Je ne suis en aucun cas affilié à la société, mais je possède la version papier de ce livre.

Il y a beaucoup de petits détails sur ce sujet qui sont très bien expliqués par Alex.

Selon la demande de Nick ... (mais tout cela n'est pas dans le chapitre)

En termes de mise à l'échelle, vous devez être brutalement honnête sur les activités qui doivent être dans le code db et celles qui doivent être dans l'application. Avez-vous déjà remarqué à quel point le code à exécution rapide a tendance à revenir à la conception pour une seule préoccupation par méthode?

Le moyen le plus simple de communiquer serait des codes d'erreur personnalisés (> 50 000). C'est aussi assez rapide. Cela signifie que vous devez synchroniser le code DB et le code d'application. Avec un code d'erreur personnalisé, vous pouvez également renvoyer des informations utiles dans la chaîne de message d'erreur. Parce que vous avez un code d'erreur strictement pour cette situation, vous pouvez écrire un analyseur dans le code d'application adapté au format de données de l'erreur.

De plus, quelles conditions d'erreur nécessitent une logique de nouvelle tentative dans la base de données? Si vous souhaitez réessayer après X secondes, vous feriez mieux de gérer cela dans le code de l'application afin que la transaction ne bloque pas autant. Si vous ne soumettez à nouveau qu'une opération DML immédiatement, la répéter dans le SP pourrait être plus efficace. Gardez à l'esprit, cependant, que vous devrez éventuellement dupliquer du code ou ajouter une couche de SP pour effectuer une nouvelle tentative.

Vraiment, c'est actuellement le plus gros problème avec la logique TRY ... CATCH dans SQL Server pour le moment. Cela peut être fait, mais c'est un peu un oaf. Recherchez certaines améliorations à venir dans SQL Server 2012, en particulier la relance des exceptions système (en conservant le numéro d'erreur d'origine). En outre, il existe FORMATMESSAGE , qui ajoute une certaine flexibilité dans la création de messages d'erreur, en particulier à des fins de journalisation.

Phil Helmer
la source
De bons conseils et un très bon livre!
Marian
Red Gate propose plusieurs livres électroniques gratuits extrêmement utiles, et celui-ci est certainement l'un des meilleurs. Grande suggestion.
Matt M
Tous leurs livres ne le font pas, mais la version gratuite du livre "Defensive ..." de Kuznetsov ne contient pas les 2 derniers chapitres sur les niveaux d'isolement des transactions et le développement de modifications qui survivent à la concurrence. Pour moi. le contenu y valait l'achat.
Phil Helmer
7

Ceci est notre modèle (enregistrement des erreurs supprimé)

Remarques:

  • Sans XACT_ABORT, tous les débuts et validations / restaurations de TXN doivent être associés
  • Un commit diminue @@ TRANCOUNT
  • Une restauration ramène @@ TRANCOUNT à zéro pour que vous obteniez l'erreur 266
  • Vous ne pouvez pas ROLLBACK la couche actuelle uniquement (par exemple décrémenter @@ TRANCOUNT lors de la restauration)
  • XACT_ABORT supprime l'erreur 266
  • Chaque proc stocké doit être conforme au même modèle afin que chaque appel soit atomique
  • La vérification de restauration est en fait redondante en raison de XACT_ABORT. Cependant, cela me fait me sentir mieux, semble étrange sans et permet des situations où vous ne le voulez pas
  • Cela permet aux TXN côté client (comme LINQ)
  • Remus Rusanu a un shell similaire qui utilise des points de sauvegarde. Je préfère un appel DB atomique et n'utilise pas de mises à jour partielles comme leur article

... alors ne créez pas plus de TXN que vous n'en avez besoin

cependant,

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
gbn
la source
que faire si @@ TRANCOUNT est supérieur à 0? vous ne faites aucun travail ou avez des commentaires?
kacalapy
@kacalapy: Il n'y a pas une telle chose comme une transaction imbriquée afin de ne pas commencer une autre scribd.com/doc/49579859/33/Nested-Transactions-Are-Real
gbn
3

J'utilise Try / Catch, mais je recueille aussi autant d'informations que possible et je les écris dans un journal d'erreurs APRÈS la restauration. Dans cet exemple, "LogEvent" est une procédure stockée qui écrit dans une table EventLog, contenant les détails de ce qui s'est passé. GetErrorInfo () est un appel de fonction qui renvoie le message d'erreur exact.

Lorsqu'une erreur se produit, les informations sont collectées, la procédure passe à la section de gestion des erreurs et effectue une restauration. Les informations sont écrites dans le journal, puis la procédure se termine.

Compte tenu des appels de procédure / fonction supplémentaires impliqués, cela semble un peu exagéré. TOUTEFOIS, cette méthode est extrêmement utile lorsque vous essayez de déboguer le problème.

exec LogEvent @Process, @Database, 'Tentative to insert blah blah blah'
COMMENCER ESSAYER
  insérer dans MyTable
  sélectionnez Valeurs
    de MyOtherTable

  sélectionnez @rowcount = @@ ROWCOUNT
FIN DE L'ESSAI
-- La gestion des erreurs
COMMENCER LA CAPTURE
  sélectionnez @error = ERROR_NUMBER (),
         @rowcount = -1,
         @TableAction = 'insert',
         @TableName = @Database + '.MyTable',
         @AdditionalInfo = '(Tentative d'insertion bla bla bla)' + dbo.GetErrorInfo ()
   GOTO TableAccessError
FIN DE CAPTURE

.
.
.
.

TableAccessError:
SI (@@ TRANCOUNT> 0) ROLLBACK
sélectionnez @output = upper (@TableAction) + 
       'ERREUR - Une erreur s'est produite pendant' + 
       cas (@TableAction)
         quand «mettre à jour» puis «mettre à jour»
         quand "supprimer" puis "supprimer"
         else @TableAction + 'ing'
       fin + 
       'records' + 
       cas (@TableAction) 
         quand 'sélectionner' puis 'de' 
         quand «mettre à jour» puis «dans» 
         quand "insérer" puis "dans"
         sinon 'de'   
         fin + 
         'la table' + @TableName + '.'
sélectionnez @output = @output + '@@ ERROR:' + convert (varchar (8), @ error) 
sélectionnez @output = @output + '@@ ROWCOUNT:' + convert (varchar (8), @ rowcount) 

sélectionnez @output = @output + isnull (@AdditionalInfo, '')
exec LogEvent @Process, @Database, @Output
RAISERROR (@ output, 16,1) avec log
sélectionnez @ReturnCode = -1
GOTO THE_EXIT


datagod
la source