Verwenden von FLOATs mit RAISERROR

11

Ich verwende RAISERROR()einige grundlegende Unit-Testing-Funktionen (wie hier ), bin jedoch frustriert über die Unfähigkeit, sie FLOATsin der Fehlermeldung zu verwenden. Ich weiß, dass ich den Float in einen String RAISERRORumwandeln kann, aber ich verwende ihn in jedem einzelnen Komponententest. Ich möchte nicht für jeden Test eine weitere Codezeile hinzufügen. (Meine Unit-Tests sind bereits wortreich genug!) Gibt es eine Möglichkeit, eine Inline- Umwandlung / Konvertierung innerhalb der RAISERRORParameterliste durchzuführen ? Oder gibt es einen anderen Weg, um diesen Mangel zu umgehen?

Update: Letztendlich wünschte ich mir also, ich könnte Folgendes tun:

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

Behandelt RAISERROR% f leider nicht oder schwebt im Allgemeinen. Also muss ich das stattdessen machen:

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

... was nur wie ein Durcheinander aussieht, wenn es über Dutzende von Unit-Tests verstreut ist. Also möchte ich es auf so etwas reduzieren:

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

Aber das bringt mir eine Incorrect syntax near 'CAST'Nachricht. Ich verstehe nicht, warum das illegal ist, aber es ist. Gibt es einen anderen "One Liner", den ich stattdessen hier verwenden könnte?

kmote
quelle
Könnten Sie bitte mehr erklären?
NoChance

Antworten:

12

Leider können Sie aus irgendeinem Grund in diesem Kontext keine Inline-Konvertierung durchführen und RAISERRORunterstützen floatsie aus irgendeinem Grund nicht direkt .

Der Vollständigkeit halber ist hier der relevante Ausschnitt aus MSDN , den Sie sicher bereits gesehen haben (Hinweis: Es ist der gleiche Text in allen Versionen der Dokumentation von 2005 bis 2012):

Jeder Substitutionsparameter kann eine lokale Variable oder einer dieser Datentypen sein: tinyint , smallint , int , char , varchar , nchar , nvarchar , binär oder varbinary .


Die einzig vernünftige Lösung, die ich mir vorstellen kann, wäre, eine gespeicherte Prozedur zu schreiben, um den RAISERRORAufruf zu beenden . Hier ist ein Ausgangspunkt:

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

Leider gibt es keine einfache Möglichkeit, dies für eine beliebige Anzahl von Parametern zu skalieren ... Dies könnte wahrscheinlich mit verschlungenem verschachteltem dynamischem SQL geschehen, dessen Debuggen Spaß machen würde. Ich werde das als Übung für den Leser belassen.

Ich habe sql_variantauf der Annahme , dass für die Code - Uniformität Gründe das gleiche Verfahren überall dort eingesetzt werden würde, auch für Werttypen , die sich direkt unterstützt RAISERROR. Dies kann auch als temporär gespeicherte Prozedur erstellt werden, wenn dies angemessen ist.

So würde die Verwendung dieses Verfahrens aussehen:

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;

Ausgabe:

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)

Das Nettoergebnis ist also, dass Sie keine Formatierungsfähigkeit für Floats erhalten (rollen Sie Ihre eigenen), aber Sie erhalten die Möglichkeit, diese auszugeben (auch dezimal / numerisch!), Während Sie die Formatierungsfähigkeit für die anderen Typen beibehalten.

Jon Seigel
quelle
Wow, das ist einfach großartig! Ich hatte überlegt, so etwas zu tun, war sql_variantmir aber nicht bewusst , also blieb ich bei der Argumentationsliste und nahm an, dass dies nicht möglich war. Sie haben mir heute etwas sehr Nützliches beigebracht. Vielen Dank!
kmote
@kmote: Kein Problem; froh, dass ich helfen konnte.
Jon Seigel