Использование FLOATs с RAISERROR

11

Я использую RAISERROR()для обеспечения некоторых базовых функций модульного тестирования (как здесь ), но я разочарован невозможностью использовать FLOATsв сообщении об ошибке. Я знаю, что могу преобразовать float в строку, но я использую RAISERRORв каждом модульном тесте, я не хочу добавлять еще одну строку кода для каждого теста. (Мои модульные тесты уже достаточно многословны!) Есть ли способ выполнить встроенное приведение / преобразование в RAISERRORсписке параметров? Или есть другой способ обойти этот недостаток?

Обновление: так что, в конечном итоге, я хотел бы сделать следующее:

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

К сожалению, RAISERRORне обрабатывает% f и не плавает вообще. Поэтому я должен сделать это вместо:

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

... которая выглядит как беспорядок, когда она разбросана по десяткам юнит-тестов. Поэтому я хотел бы свести это к следующему:

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

Но это дает мне Incorrect syntax near 'CAST'сообщение. Я не понимаю, почему это незаконно, но это так. Есть ли другой «один вкладыш», который я мог бы использовать здесь вместо этого?

kmote
источник
Не могли бы вы объяснить больше, пожалуйста?
NoChance

Ответы:

12

К сожалению, по какой-то причине вы не можете выполнить встроенное преобразование в этом контексте и RAISERRORне поддерживает напрямую float, опять же, по любой причине.

Для полноты этого ответа вот соответствующий фрагмент из MSDN , который, я уверен, вы уже видели (примечание: это один и тот же текст во всех версиях документации с 2005 по 2012 годы):

Каждый параметр подстановки может быть локальной переменной или любым из этих типов данных: tinyint , smallint , int , char , varchar , nchar , nvarchar , двоичный или varbinary .


Единственное разумное решение, о котором я могу подумать, - это написать хранимую процедуру, чтобы обернуть RAISERRORвызов. Вот отправная точка:

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

К сожалению, нет простого способа масштабировать это для произвольного числа параметров ... Это, вероятно, можно сделать с помощью сложного вложенного динамического SQL, который было бы интересно отлаживать. Я оставлю это как упражнение для читателя.

Я использовал sql_variantпредположение, что по причинам единообразия кода одна и та же процедура будет использоваться везде, даже для типов значений, которые поддерживаются напрямую RAISERROR. Кроме того, это может быть создано как временная хранимая процедура, если это уместно.

Вот как будет выглядеть эта процедура:

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;

Выход:

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)

Таким образом, общий результат заключается в том, что вы не получаете возможность форматирования для чисел с плавающей запятой (сверните свое собственное), но вы получаете возможность выводить их (также десятичную / числовую!), Сохраняя возможность форматирования для других типов.

Джон Сайгель
источник
Вау, это просто великолепно! Я подумывал сделать что-то подобное, но ничего не знал sql_variant, поэтому застрял в списке аргументов и предположил, что это невозможно. Вы научили меня чему-то очень полезному сегодня. Большое спасибо!
км
@kmote: нет проблем; Рад, что смог помочь.
Джон Зигель