2009-06-29 2 views
25

J'ai une procédure stockée qui effectue une validation de paramètre et devrait échouer et arrêter l'exécution si le paramètre n'est pas valide.La "bonne" façon de faire la validation des paramètres de procédure stockée

Ma première approche pour la vérification d'erreurs ressemblait à ceci:

create proc spBaz 
(
    @fooInt int = 0, 
    @fooString varchar(10) = null, 
    @barInt int = 0, 
    @barString varchar(10) = null 
) 
as 
begin 
    if (@fooInt = 0 and (@fooString is null or @fooString = '')) 
    raiserror('invalid parameter: foo', 18, 0) 

    if (@barInt = 0 and (@barString is null or @barString = '')) 
    raiserror('invalid parameter: bar', 18, 0) 

    print 'validation succeeded' 
    -- do some work 
end 

Cela ne fait pas le tour puisque la sévérité 18 ne se limite pas à l'exécution et « validation réussi » est imprimé en même temps que les messages d'erreur.

Je sais que je pourrais simplement ajouter un retour après chaque raiserror mais ressemble un peu laid pour moi:

if (@fooInt = 0 and (@fooString is null or @fooString = '')) 
    begin 
    raiserror('invalid parameter: foo', 18, 0) 
    return 
    end 

    ... 

    print 'validation succeeded' 
    -- do some work 

Depuis des erreurs avec sévérité 11 et plus sont pris dans un bloc try/catch Une autre approche je l'ai testé était d'encapsuler ma vérification d'erreur à l'intérieur d'un tel bloc try/catch. Le problème était que l'erreur avait été avalée et n'avait pas été envoyée au client. J'ai donc fait quelques recherches et trouvé un moyen de rethrow l'erreur:

Je ne suis toujours pas satisfait de cette approche donc je vous demande:

Comment votre validation des paramètres ressemble? Y at-il une sorte de «meilleure pratique» pour faire ce genre de vérification?

Répondre

37

Je ne pense pas qu'il existe une seule "bonne" façon de le faire.

Ma propre préférence serait similaire à votre deuxième exemple, mais avec une étape de validation distincte pour chaque paramètre et des messages d'erreur plus explicites. Comme vous le dites, c'est un peu lourd et moche, mais l'intention du code est évidente pour quiconque le lit, et le travail est fait.

IF (ISNULL(@fooInt, 0) = 0) 
BEGIN 
    RAISERROR('Invalid parameter: @fooInt cannot be NULL or zero', 18, 0) 
    RETURN 
END 

IF (ISNULL(@fooString, '') = '') 
BEGIN 
    RAISERROR('Invalid parameter: @fooString cannot be NULL or empty', 18, 0) 
    RETURN 
END 
+0

Y a-t-il une raison pour laquelle vous avez utilisé IF (ISNULL (@fooString, '') = '') plutôt que IF (@fooString est null)? – macleojw

+9

@macleojw: Il vérifie null et '' à la fois .. intelligent :) – VVS

+6

Le second validateur a une syntaxe invalide: 'RAISEERROR'. Il devrait y avoir juste un 'e'. Ce qui est amusant, c'est qu'en anglais c'est correct car 'raise + error' a double 'e' mais pas en langage MS SQL. –

1

Nous évitons normalement RaiseError() et retourne une valeur qui indique une erreur, par exemple un nombre négatif:

if <errorcondition> 
    return -1 

Ou passer le résultat de deux paramètres sur:

create procedure dbo.TestProc 
    .... 
    @result int output, 
    @errormessage varchar(256) output 
as 
set @result = -99 
set @errormessage = null 
.... 
if <errorcondition> 
    begin 
    set @result = -1 
    set @errormessage = 'Condition failed' 
    return @result 
    end 
+0

Pourquoi préférez-vous revenir sur raiseerror()? – macleojw

+0

Raiseerror est imprévisible (peut continuer l'exécution!) Et pas eveyr client traite avec elle de la même manière. Un client Perl pourrait mourir! – Andomar

0

Je préfère revenir le plus tôt possible, et ne pas pointer du tout sur le même point à la fin de la procédure. J'ai pris cette habitude de faire l'assemblage, il y a des années. De plus, je reviens toujours une valeur:

RETURN 10 

L'application affiche une erreur fatale sur des nombres positifs, et affiche le message d'avertissement de l'utilisateur sur des valeurs négatives.

Nous renvoyons toujours un paramètre OUTPUT avec le texte du message d'erreur.

exemple:

IF ~error~ 
BEGIN 
    --if it is possible to be within a transaction, so any error logging is not ROLLBACK later 
    IF XACT_STATE()!=0 
    BEGIN 
     ROLLBACK 
    END 

    SET @OutputErrMsg='your message here!!' 
    INSERT INTO ErrorLog (....) VALUES (.... @OutputErrMsg) 
    RETURN 10 

END 
+0

Je veux utiliser le sproc dans un autre sproc et je veux faire aussi peu de vérification d'erreur que possible, donc élever une erreur et l'attraper dans le sproc externe semble être la meilleure façon de le faire. – VVS

+1

un jour où cette procédure est appelée à partir d'un autre endroit, j'espère qu'ils se souviennent d'attraper les erreurs aussi. Je pense qu'il est préférable d'attraper toutes les erreurs au fur et à mesure, de les traiter localement et de renvoyer les informations appropriées. –

1

Comme vous pouvez le voir dans cette histoire de réponse que je suivi cette question et réponse acceptée, puis a procédé à « inventer » une solution qui était essentiellement le même que votre deuxième approche.

La caféine est ma principale source d'énergie, étant donné que je passe la plus grande partie de ma vie à m'endormir car je passe beaucoup trop de temps à coder; ainsi je n'ai pas réalisé mon faux-pas jusqu'à ce que vous l'ayez signalé correctement. Par conséquent, pour l'anecdote, je préfère votre deuxième approche: utiliser un SP pour augmenter l'erreur actuelle, puis utiliser un TRY/CATCH autour de la validation de votre paramètre.

Cela réduit le besoin de tous les blocs IF/BEGIN/END et réduit donc le nombre de lignes ainsi que le focus sur la validation. Lors de la lecture du code pour le SP, il est important de pouvoir voir les tests effectués sur les paramètres; tous les bourrages syntaxiques supplémentaires pour satisfaire l'analyseur SQL ne font que gêner, à mon avis.

+0

Y a-t-il une raison pour laquelle vous copiez ma seconde approche et que j'ai même réinventé le SP-Rethrow que j'ai utilisé à l'origine? – VVS

+0

@VVS - oh chérie tu as raison! Ce n'était pas délibéré, j'ai lu la question et les réponses. Puis, par manque de sommeil j'ai rapidement oublié toute la gamme d'options affichées ici et d'abord allé pour le résultat if/begin/end puis 'découvert' cette approche - en oubliant que c'est l'une des options que vous avez essayé. Toutes mes excuses! –

0

J'utilise toujours le bit de paramètre @Is_Success comme SORTIE. Donc, si j'ai une erreur, alors @ Is_success = 0. Lorsque la procédure parent vérifie que @ Is_Success = 0, elle annule sa transaction (avec les transactions enfant) et envoie un message d'erreur de @Error_Message au client.

+1

Hm, cela n'a de sens que si la valeur de retour du SP est utilisée autrement. Ou y a-t-il une bonne raison pour ne pas simplement faire "RETURN x"? – VVS

+0

Vous avez toujours deux possibilités d'erreurs: erreur SQL (par exemple l'analyse XML) et logique (par exemple erreur de contrainte). Si vous ne faites pas de différence entre eux, vous perdez le contrôle du comportement de l'application. Si vous enregistrez des erreurs en dehors de SQL, par exemple dans le système de fichiers, votre application devrait obtenir le type d'erreur. – Dalex

Questions connexes