2009-06-29 11 views
25

Tengo un procedimiento almacenado que realiza la validación de algunos parámetros y debe fallar y detener la ejecución si el parámetro no es válido.La forma "correcta" de hacer la validación del parámetro de procedimiento almacenado

Mi primer acercamiento para verificación de errores se veía así:

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 

Esto no hizo el truco ya que la gravedad 18 no se detiene la ejecución y 'validación tuvo éxito' se imprime junto con los mensajes de error.

Sé que simplemente podría añadir un retorno después de cada raiserror pero esto parece un poco feo para mí:

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

    ... 

    print 'validation succeeded' 
    -- do some work 

Dado que los errores con gravedad 11 y superiores se encuentran atrapados dentro de un bloque try/catch otro enfoque Probé fue para encapsular mi error comprobando dentro de tal bloque try/catch. El problema fue que el error se tragó y no se envió al cliente en absoluto. Así que hice algunas investigaciones y encontró una manera de rethrow el error:

Todavía no estoy feliz con este enfoque por lo que te pido:

¿Cómo su validación de parámetros parecen? ¿Hay algún tipo de "mejor práctica" para hacer este tipo de control?

Respuesta

37

No creo que haya una sola forma "correcta" de hacer esto.

Mi propia preferencia sería similar a su segundo ejemplo, pero con un paso de validación por separado para cada parámetro y mensajes de error más explícitos.

Como dices, es un poco engorroso y feo, pero la intención del código es obvia para cualquiera que lo lea, y hace el trabajo bien.

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

¿Hay alguna razón por la que haya utilizado IF (ISNULL (@fooString, '') = '') en lugar de IF (@fooString es nulo)? – macleojw

+9

@macleojw: Comprueba nulo y "al mismo tiempo ... inteligente :) – VVS

+6

El segundo validador tiene una sintaxis no válida: 'RAISEERROR'. Debe haber solo una 'e'. Es curioso que en inglés sea correcto ya que 'raise + error' tiene doble 'e' pero no en lenguaje MS SQL. –

1

normalmente Evitamos raiseError() y devolver un valor que indica un error, por ejemplo, un número negativo:

if <errorcondition> 
    return -1 

o pasar el resultado en dos parámetros 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 
+0

¿Por qué prefieres return over raiseerror()? – macleojw

+0

Raiseerror es impredecible (¡puede continuar la ejecución!) Y no todos los clientes tratan con él de la misma manera. ¡Un cliente perl podría morir! – Andomar

0

Prefiero volver tan pronto como sea posible, y no veo que todo vuelva a salir desde el mismo punto al final del procedimiento. Recogí este hábito haciendo asamblea, hace años. Además, siempre vuelvo un valor:

RETURN 10 

La aplicación mostrará un error grave en números positivos, y mostrará el mensaje de advertencia de usuario en valores negativos.

Siempre devolvemos un parámetro OUTPUT con el texto del mensaje de error.

ejemplo:

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

Quiero usar el sproc dentro de otro sproc y quiero hacer la menor verificación de errores posible, así que levantar un error y capturarlo en el sproc externo parece ser la mejor manera de hacerlo. – VVS

+1

algún día cuando se llame a este procedimiento desde otra ubicación, espero que recuerden detectar los errores también. Creo que es mejor detectar todos los errores a medida que ocurren, tratarlos localmente y devolver la información adecuada. –

1

Como se puede ver en esta historia respuesta que siguió a esta pregunta y respuesta aceptada y, a continuación, procedió a 'inventar' una solución que era básicamente el mismo que el segundo enfoque.

La cafeína es mi principal fuente de energía, debido al hecho de que paso la mayor parte de mi vida medio dormida porque paso demasiado tiempo codificando; por lo tanto, no me di cuenta de mi faux-pas hasta que lo señaló correctamente.

Por lo tanto, para el registro, prefiero su segundo enfoque: usar un SP para aumentar el error actual, y luego usar un TRY/CATCH alrededor de la validación de su parámetro.

Reduce la necesidad de todos los bloques IF/BEGIN/END y, por lo tanto, reduce el recuento de líneas y vuelve a centrar la validación. Al leer el código para el SP, es importante poder ver las pruebas que se realizan en los parámetros; toda la pelusa sintáctica adicional para satisfacer el analizador de SQL simplemente se interpone, en mi opinión.

+0

¿Hay alguna razón por la que está copiando mi segundo enfoque e incluso reinventó el Rethrow-SP que utilicé originalmente? – VVS

+0

@VVS - ¡Dios mío, tienes razón! No fue deliberado, leí la pregunta y las respuestas. Luego, debido a la falta de sueño, rápidamente olvidé toda la gama de opciones mostradas aquí e inicialmente fui al resultado de si/comienzo/final y luego 'descubrí' este enfoque, olvidando que esta es una de las opciones que había probado. Disculpas! –

0

Siempre uso el parámetro @Is_Success bit como OUTPUT. Entonces, si tengo un error, entonces @ Is_success = 0. Cuando el procedimiento principal comprueba que @ Is_Success = 0, entonces retrotrae su transacción (con transacciones secundarias) y envía un mensaje de error desde @Error_Message al cliente.

+1

Hm, eso solo tiene sentido, si el valor de retorno del SP se usa de otra manera. ¿O hay una buena razón por la cual no solo haces "RETORNO x"? – VVS

+0

Siempre tiene dos posibilidades para errores: error SQL (por ejemplo, análisis XML) y lógico (por ejemplo, error de restricción). Si no haces diferencia entre ellos, pierdes el control sobre el comportamiento de la aplicación. Si registra errores fuera de SQL, por ejemplo, en el sistema de archivos, su aplicación debería obtener el tipo de error. – Dalex

Cuestiones relacionadas