Utilisation de FLOAT avec RAISERROR

11

J'utilise RAISERROR()pour fournir des fonctionnalités de test unitaire de base (comme ici ), mais je suis frustré par l'impossibilité d'utiliser FLOATsdans le message d'erreur. Je sais que je peux convertir le flotteur en une chaîne, mais j'utilise RAISERRORdans chaque test unitaire, je ne veux pas ajouter une autre ligne de code pour chaque test. (Mes tests unitaires sont déjà assez verbeux!) Est - il possible d'effectuer une ligne CAST / convert au sein de la RAISERRORliste des paramètres? Ou existe-t-il un autre moyen de contourner cette carence?

Mise à jour: Donc, finalement, ce que j'aimerais pouvoir faire, c'est ceci:

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

Malheureusement, RAISERRORne gère pas% f ou ne flotte pas en général. Je dois donc faire ceci à la place:

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

... qui ressemble à un gâchis quand il est dispersé à travers des dizaines de tests unitaires. Je voudrais donc le résumer à quelque chose comme ceci:

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

Mais cela me fait Incorrect syntax near 'CAST'passer un message. Je ne comprends pas pourquoi c'est illégal, mais ça l'est. Y a-t-il un autre "one liner" que je pourrais utiliser ici à la place?

kmote
la source
Pourriez-vous expliquer plus s'il vous plaît?
NoChance

Réponses:

12

Malheureusement, pour quelque raison que ce soit, vous ne pouvez pas effectuer de conversion en ligne dans ce contexte et RAISERRORne prend pas directement en charge float, encore une fois, pour quelque raison que ce soit.

Pour l'exhaustivité de cette réponse, voici l'extrait pertinent de MSDN , que je suis sûr que vous avez déjà vu (remarque: c'est le même texte dans toutes les versions de la documentation de 2005 à 2012):

Chaque paramètre de substitution peut être une variable locale ou l'un de ces types de données: tinyint , smallint , int , char , varchar , nchar , nvarchar , binary ou varbinary .


La seule solution raisonnable à laquelle je peux penser serait d'écrire une procédure stockée pour encapsuler l' RAISERRORappel. Voici un point de départ:

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

Malheureusement, il n'y a pas de moyen facile de mettre à l'échelle cela pour un nombre arbitraire de paramètres ... Cela pourrait probablement être fait en utilisant du SQL dynamique imbriqué alambiqué, ce qui serait amusant à déboguer. Je vais laisser cela comme un exercice pour le lecteur.

J'ai utilisé sql_variantl'hypothèse que pour des raisons d'uniformité du code, la même procédure serait utilisée partout, même pour les types de valeurs qui sont directement pris en charge par RAISERROR. En outre, cela peut être créé en tant que procédure stockée temporaire si cela est approprié.

Voici à quoi ressemblerait cette procédure:

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;

Production:

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)

Ainsi, le résultat net est que vous n'obtenez pas de capacité de formatage pour les flottants (lancez le vôtre), mais vous gagnez la possibilité de les sortir (décimal / numérique, aussi!) Tout en conservant la capacité de formatage pour les autres types.

Jon Seigel
la source
Wow, c'est tout simplement superbe! J'avais envisagé de faire quelque chose comme ça, mais sql_variantje n'étais pas au courant , donc j'étais coincé sur la liste des arguments et j'ai supposé que ce n'était pas possible. Vous m'avez appris quelque chose de très utile aujourd'hui. Merci beaucoup!
kmote
@kmote: Pas de problème; heureux d'avoir pu aider.
Jon Seigel