Usando FLOATs com RAISERROR

11

Estou usando RAISERROR()para fornecer algumas funcionalidades básicas de teste de unidade (como aqui ), mas estou frustrado com a incapacidade de usar FLOATsna mensagem de erro. Eu sei que posso converter o float em uma string, mas estou usando RAISERRORem cada teste de unidade, não quero adicionar outra linha de código para cada teste. (Meus testes de unidade já estão bastante prolixo!) Existe uma maneira de executar uma linha elenco / converso dentro da RAISERRORlista de parâmetros? Ou existe outra maneira de contornar essa deficiência?

Atualização: Então, em última análise, o que eu gostaria de poder fazer é o seguinte:

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

Infelizmente, RAISERRORnão lida com% f ou flutua em geral. Então, eu tenho que fazer isso:

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

... que parece uma bagunça quando está espalhada por dezenas de testes de unidade. Então, eu gostaria de resumir a algo como isto:

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

Mas isso me dá uma Incorrect syntax near 'CAST'mensagem. Não entendo por que isso é ilegal, mas é. Existe outro "one liner" que eu poderia usar aqui?

kmote
fonte
Você poderia explicar mais, por favor?
NoChance

Respostas:

12

Infelizmente, por qualquer motivo, você não pode fazer uma conversão embutida nesse contexto e RAISERRORnão suporta diretamente floatnovamente, por qualquer motivo.

Para uma conclusão completa desta resposta, aqui está o trecho relevante do MSDN , que eu tenho certeza que você já viu (nota: é o mesmo texto em todas as versões da documentação de 2005 a 2012):

Cada parâmetro de substituição pode ser uma variável local ou qualquer um desses tipos de dados: tinyint , smallint , int , char , varchar , nchar , nvarchar , binário ou varbinário .


A única solução razoável em que posso pensar seria escrever um procedimento armazenado para encerrar a RAISERRORchamada. Aqui está um ponto 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

Infelizmente, não há uma maneira fácil de escalar isso para um número arbitrário de parâmetros ... Provavelmente isso poderia ser feito usando SQL dinâmico aninhado complicado, o que seria divertido de depurar. Vou deixar isso como um exercício para o leitor.

I utilizado sql_variantno pressuposto de que, por razões de uniformidade código, o mesmo procedimento poderia ser usado em todos os lugares, mesmo para tipos de valor que são suportados diretamente pelo RAISERROR. Além disso, isso pode ser criado como um procedimento armazenado temporário , se for apropriado.

Aqui está a aparência desse procedimento:

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;

Resultado:

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)

Portanto, o resultado líquido é que você não tem capacidade de formatação para carros alegóricos (faça o seu próprio), mas ganha a capacidade de produzi-los (decimal / numérico também!), Mantendo a capacidade de formatação para os outros tipos.

Jon Seigel
fonte
Uau, isso é excelente! Eu tinha pensado em fazer algo assim, mas não sabia sql_variant, então fiquei preso na lista de argumentos e presumi que não era possível. Você me ensinou algo muito útil hoje. Muito obrigado!
precisa
@kmote: Não há problema; feliz por poder ajudar.
21912 Jon Jonseig