try transaction stored generate error_message error ejemplos catch begin sql sql-server stored-procedures error-handling

sql - transaction - La forma "correcta" de hacer validaciones de parámetros de procedimientos almacenados



throw exception in sql server stored procedure (5)

Como puede ver en este historial de respuestas, seguí esta pregunta y acepté la respuesta, y luego procedí a ''inventar'' una solución que era básicamente la misma que su 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 elevar 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.

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.

Mi primer enfoque para la comprobació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 funcionó, ya que la gravedad 18 no detiene la ejecución y la ''validación tuvo éxito'' se imprime junto con los mensajes de error.

Sé que podría simplemente agregar una devolución después de cada raiserror pero esto se ve algo 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 una gravedad 11 y superior se detectan dentro de un bloque try / catch, otro enfoque que probé fue encapsular mi comprobación de errores dentro de dicho bloque try / catch. El problema fue que el error se tragó y no se envió al cliente en absoluto. Así que investigué y encontré una manera de rethrow a rethrow el error:

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

Todavía no estoy contento con este enfoque, así que te estoy preguntando:

¿Cómo es la validación de tu parámetro? ¿Hay algún tipo de "mejor práctica" para hacer este tipo de control?


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


Normalmente evitamos raiseerror () y devolvemos un valor que indica un error, por ejemplo, un número negativo:

if <errorcondition> return -1

O pase el resultado en dos parámetros de salida:

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


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 devuelvo un valor:

RETURN 10

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

Siempre pasamos 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


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.