Używanie PŁYWAKÓW z RAISERROR

11

Korzystam RAISERROR()z niektórych podstawowych funkcji testowania jednostek (jak tutaj ), ale denerwuje mnie brak możliwości użycia FLOATsw komunikacie o błędzie. Wiem, że mogę rzutować zmiennoprzecinkowe na ciąg, ale używam RAISERRORw każdym teście jednostkowym, nie chcę dodawać kolejnej linii kodu dla każdego testu. (Moje testy jednostkowe są już wystarczająco pracochłonne!) Czy istnieje sposób wykonania wbudowanego rzutowania / konwersji na RAISERRORliście parametrów? Czy jest jakiś sposób na obejście tego niedoboru?

Aktualizacja: Tak więc ostatecznie chciałbym to zrobić:

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

Niestety, RAISERRORnie obsługuje% f lub ogólnie jest zmiennoprzecinkowy. Zamiast tego muszę to zrobić:

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

... który po prostu wygląda na bałagan, gdy jest rozproszony przez dziesiątki testów jednostkowych. Chciałbym sprowadzić to do czegoś takiego:

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

Ale to daje mi Incorrect syntax near 'CAST'wiadomość. Nie rozumiem, dlaczego to nielegalne, ale tak jest. Czy może być tutaj inny „jeden liniowiec”?

kmote
źródło
Czy mógłbyś wyjaśnić więcej?
NoChance

Odpowiedzi:

12

Niestety, z jakiegokolwiek powodu, nie można wykonać bezpośredniej konwersji w tym kontekście i ponownie RAISERRORnie obsługuje bezpośrednio float, z jakiegokolwiek powodu.

Aby uzyskać kompletność tej odpowiedzi, oto odpowiedni fragment z MSDN , który na pewno już widziałeś (uwaga: to ten sam tekst we wszystkich wersjach dokumentacji od 2005 do 2012 roku):

Każdy parametr podstawienia może być zmienną lokalną lub dowolnym z tych typów danych: tinyint , smallint , int , char , varchar , nchar , nvarchar , binary lub varbinary .


Jedynym rozsądnym rozwiązaniem, jakie mogę wymyślić, byłoby napisanie procedury składowanej, aby zakończyć RAISERRORpołączenie. Oto punkt wyjścia:

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

Niestety, nie ma łatwego sposobu na skalowanie tego dla dowolnej liczby parametrów ... Prawdopodobnie można to zrobić za pomocą zawiniętego zagnieżdżonego dynamicznego SQL, co byłoby przyjemne w debugowaniu. Zostawię to jako ćwiczenie dla czytelnika.

Kiedyś sql_variantna założeniu, że ze względu na kod jednorodności, ta sama procedura będzie używane wszędzie, nawet dla typów wartości, które obsługiwane bezpośrednio RAISERROR. Można to również utworzyć jako tymczasową procedurę składowaną, jeśli jest to właściwe.

Oto jak wyglądałaby ta procedura:

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;

Wynik:

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)

Tak więc wynik netto jest taki, że nie masz możliwości formatowania dla liczb zmiennoprzecinkowych (rzuć własne), ale zyskujesz zdolność do ich wyprowadzania (również dziesiętne / numeryczne!), Zachowując jednocześnie zdolność formatowania dla innych typów.

Jon Seigel
źródło
Wow, to jest po prostu wspaniałe! Zastanawiałem się nad zrobieniem czegoś takiego, ale nie byłem tego świadomy sql_variant, więc utknąłem na liście argumentów i założyłem, że to niemożliwe. Nauczyłeś mnie dziś czegoś bardzo przydatnego. Dziękuję Ci bardzo!
kmote
@kmote: Bez problemu; Cieszę się, że mogłem pomóc.
Jon Seigel