Uso de flotadores con RAISERROR

11

Estoy usando RAISERROR()para proporcionar algunas funciones básicas de Prueba de Unidad (como aquí ), pero estoy frustrado por la incapacidad de usar FLOATsen el mensaje de error. Sé que puedo convertir el flotante en una cadena, pero lo estoy usando RAISERRORen cada prueba de unidad individual, no quiero agregar otra línea de código para cada prueba. (¡Mis pruebas unitarias ya son lo suficientemente detalladas!) ¿Hay alguna forma de realizar una conversión / conversión en línea dentro de la RAISERRORlista de parámetros? ¿O hay otra forma de evitar esta deficiencia?

Actualización: Entonces, en última instancia, lo que desearía poder hacer es esto:

RAISERROR('Unit Test FAILED! %f', 11, 0, @floatParm)

Desafortunadamente, RAISERRORno maneja% f o flota en general. Entonces tengo que hacer esto en su lugar:

DECLARE @str VARCHAR(40) = CAST(@floatParm AS VARCHAR(40))
RAISERROR('Unit Test FAILED! %s', 11, 0, @str)

... que parece un desastre cuando se dispersa a través de docenas de pruebas unitarias. Así que me gustaría resumirlo en algo como esto:

RAISERROR('Unit Test FAILED! %s', 11, 0, CAST(@floatParm AS VARCHAR(40))

Pero eso me da un Incorrect syntax near 'CAST'mensaje. No entiendo por qué eso es ilegal, pero lo es. ¿Hay otro "un revestimiento" que podría usar aquí?

kmote
fuente
¿Podría explicar más por favor?
NoChance

Respuestas:

12

Desafortunadamente, por cualquier razón, no puede hacer una conversión en línea en ese contexto, y RAISERRORno admite directamente float, de nuevo, por cualquier razón.

Para completar esta respuesta, aquí está el fragmento relevante de MSDN , que estoy seguro de que ya has visto (nota: es el mismo texto en todas las versiones de la documentación de 2005 a 2012):

Cada parámetro de sustitución puede ser una variable local o cualquiera de estos tipos de datos: tinyint , smallint , int , char , varchar , nchar , nvarchar , binary o varbinary .


La única solución razonable que se me ocurre sería escribir un procedimiento almacenado para finalizar la RAISERRORllamada. Aquí hay un punto de partida:

CREATE PROCEDURE [dbo].[MyRaiserror]
(
    @message nvarchar(2048),
    @severity tinyint,
    @state tinyint,
    @arg0 sql_variant = NULL
)
AS
BEGIN

    DECLARE @msg nvarchar(MAX) = REPLACE(@message, '%f', '%s');
    DECLARE @sql nvarchar(MAX) = N'RAISERROR(@msg, @severity, @state';

    DECLARE @int0 int, @char0 nvarchar(MAX), @bin0 varbinary(MAX);

    IF (@arg0 IS NOT NULL)
    BEGIN
        SET @sql += N', ';

        IF (SQL_VARIANT_PROPERTY(@arg0, 'BaseType') IN ('tinyint', 'smallint', 'int'))
        BEGIN
            SET @int0 = CONVERT(int, @arg0);
            SET @sql += N'@int0';
        END
        ELSE IF (SQL_VARIANT_PROPERTY(@arg0, 'BaseType') IN ('binary', 'varbinary'))
        BEGIN
            SET @bin0 = CONVERT(varbinary(MAX), @arg0);
            SET @sql += N'@bin0';
        END
        ELSE
        BEGIN
            SET @char0 = CONVERT(nvarchar(MAX), @arg0);
            SET @sql += N'@char0';
        END
    END

    SET @sql += N');';

    EXEC sp_executesql
        @sql,
        N'@msg nvarchar(2048), @severity tinyint, @state tinyint, @int0 int, @bin0 varbinary(MAX), @char0 nvarchar(MAX)',
        @msg, @severity, @state, @int0, @bin0, @char0;

END

Lamentablemente, no hay una manera fácil de escalar esto para un número arbitrario de parámetros ... Probablemente podría hacerse usando SQL dinámico anidado enrevesado, lo que sería divertido de depurar. Lo dejaré como ejercicio para el lector.

He utilizado sql_varianten el supuesto de que, por razones de uniformidad de código, el mismo procedimiento se utiliza en todas partes, incluso para los tipos de valores que están soportados directamente por RAISERROR. Además, esto podría crearse como un procedimiento almacenado temporal si es apropiado.

Así es como se vería este procedimiento:

DECLARE @f float = 0.02345;
DECLARE @i int = 234;
DECLARE @s varchar(20) = 'asdfasdf';
DECLARE @b binary(4) = 0xA0B1C2D3;
DECLARE @d decimal(18, 9) = 152.2323;
DECLARE @n int = NULL;

EXEC [dbo].[MyRaiserror] N'Error message with no params.', 10, 1;
EXEC [dbo].[MyRaiserror] N'Float value = %f', 10, 1, @f;
EXEC [dbo].[MyRaiserror] N'Int value = %i', 10, 1, @i;
EXEC [dbo].[MyRaiserror] N'Character value = %s', 10, 1, @s;
EXEC [dbo].[MyRaiserror] N'Binary value = %#x', 10, 1, @b;
EXEC [dbo].[MyRaiserror] N'Decimal value = %f', 10, 1, @d;
EXEC [dbo].[MyRaiserror] N'Null value = %i', 10, 1, @n;

Salida:

Error message with no params.
Float value = 0.02345
Int value = 234
Character value = asdfasdf
Binary value = 0xa0b1c2d3
Decimal value = 152.232300000
Null value = (null)

Por lo tanto, el resultado neto es que no obtienes la capacidad de formateo de flotantes (tira la tuya propia), pero sí obtienes la capacidad de generarlos (¡decimal / numérico también!) Mientras conservas la capacidad de formateo para los otros tipos.

Jon Seigel
fuente
¡Guau, eso es excelente! Había considerado hacer algo como esto, pero no lo sabía sql_variant, así que me quedé atrapado en la lista de argumentos y asumí que no era posible. Me has enseñado algo muy útil hoy. ¡Muchas gracias!
kmote
@kmote: No hay problema; Me alegro de poder ayudar.
Jon Seigel