La "bonne" façon de faire la validation des parameters de la procédure stockée

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.

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

create proc spBaz ( @fooInt int = 0, @fooSsortingng varchar(10) = null, @barInt int = 0, @barSsortingng varchar(10) = null ) as begin if (@fooInt = 0 and (@fooSsortingng is null or @fooSsortingng = '')) raiserror('invalid parameter: foo', 18, 0) if (@barInt = 0 and (@barSsortingng is null or @barSsortingng = '')) raiserror('invalid parameter: bar', 18, 0) print 'validation succeeded' -- do some work end 

Cela n'a pas fait l'affaire car la gravité 18 n'arrête pas l'exécution et la 'validation réussie' est imprimée avec les messages d'erreur.

Je sais que je pourrais simplement append un return après chaque erreur, mais cela me semble plutôt moche:

  if (@fooInt = 0 and (@fooSsortingng is null or @fooSsortingng = '')) begin raiserror('invalid parameter: foo', 18, 0) return end ... print 'validation succeeded' -- do some work 

Puisque les erreurs de gravité 11 et plus sont interceptées dans un bloc try / catch, une autre approche que j'ai testée consistait à encapsuler ma vérification d'erreur dans 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 des searchs et trouvé un moyen de relancer l'erreur:

  begin try if (@fooInt = 0 and (@fooSsortingng is null or @fooSsortingng = '')) raiserror('invalid parameter: foo', 18, 0) ... end try begin catch exec usp_RethrowError return end catch print 'validation succeeded' -- do some work 

Je ne suis toujours pas content de cette approche alors je vous request:

Comment ressemble la validation de vos parameters? Y at-il une sorte de «meilleure pratique» pour faire ce genre de vérification?

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

    Ma propre preference serait similaire à votre deuxième exemple, mais avec une étape de validation séparée pour chaque paramètre et des messages d'erreur plus explicites.

    Comme vous le dites, c'est un peu lourd et laid, 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(@fooSsortingng, '') = '') BEGIN RAISERROR('Invalid parameter: @fooSsortingng cannot be NULL or empty', 18, 0) RETURN END 

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

     if <errorcondition> return -1 

    Ou passez le résultat dans deux parameters out:

     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 

    Comme vous pouvez le voir à partir de cette réponse historique, j'ai suivi cette question et accepté la réponse, puis j'ai procédé à «inventer» une solution qui était fondamentalement la même que votre deuxième approche.

    La caféine est ma principale source d'énergie, en raison du fait que je passe la plus grande partie de ma vie à m'endormir car je passe beaucoup trop de time à coder; ainsi je n'ai pas réalisé mon faux-pas jusqu'à ce que vous l'ayez signalé correctement.

    Par conséquent, pour l'logging, je préfère votre deuxième approche: en utilisant un SP pour augmenter l'erreur actuelle, puis en utilisant un TRY / CATCH autour de votre validation de paramètre.

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

    Je préfère revenir le plus tôt possible, et ne pas pointer du tout vers 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 renvoie toujours une valeur:

     RETURN 10 

    L'application affichera une erreur fatale sur les nombres positifs et affichera le message d'avertissement de l'user sur les valeurs négatives.

    Nous renvoyons toujours un paramètre OUTPUT avec le text 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 

    J'utilise toujours le bit de paramètre @Is_Success comme OUTPUT. 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.