Menggunakan FLOAT dengan RAISERROR

11

Saya menggunakan RAISERROR()untuk menyediakan beberapa fungsionalitas Unit Testing dasar (seperti di sini ), tetapi saya frustrasi dengan ketidakmampuan untuk digunakan FLOATsdalam pesan kesalahan. Saya tahu saya bisa melemparkan float ke string, tetapi saya menggunakan RAISERRORdalam setiap unit test, saya tidak ingin menambahkan baris kode lain untuk setiap tes. (Tes unit saya sudah cukup bertele-tele!) Apakah ada cara untuk melakukan pemeran inline / konversi dalam RAISERRORdaftar parameter? Atau adakah cara lain untuk mengatasi kekurangan ini?

Pembaruan: Jadi pada akhirnya apa yang saya harap dapat saya lakukan adalah ini:

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

Sayangnya, RAISERRORtidak menangani% f atau mengapung secara umum. Jadi saya harus melakukan ini sebagai gantinya:

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

... yang hanya terlihat berantakan ketika itu tersebar melalui puluhan Tes Unit. Jadi saya ingin merebusnya menjadi seperti ini:

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

Tapi itu memberiku Incorrect syntax near 'CAST'pesan. Saya tidak mengerti mengapa itu ilegal, tetapi memang demikian. Apakah ada "satu liner" lain yang bisa saya gunakan di sini?

kmote
sumber
Bisakah Anda jelaskan lebih banyak?
NoChance

Jawaban:

12

Sayangnya, untuk alasan apa pun, Anda tidak dapat melakukan konversi inline dalam konteks itu, dan RAISERRORtidak langsung mendukung float, lagi, untuk alasan apa pun.

Untuk kelengkapan jawaban ini, berikut ini cuplikan yang relevan dari MSDN , yang saya yakin sudah Anda lihat (catatan: ini adalah teks yang sama di semua versi dokumentasi dari 2005 hingga 2012):

Setiap parameter substitusi dapat berupa variabel lokal atau salah satu dari tipe data ini: tinyint , smallint , int , char , varchar , nchar , nvarchar , binary , atau varbinary .


Satu-satunya solusi yang masuk akal yang dapat saya pikirkan adalah menulis prosedur tersimpan untuk menutup RAISERRORpanggilan. Inilah titik awalnya:

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

Sayangnya, tidak ada cara mudah untuk skala ini untuk sejumlah parameter sewenang-wenang ... Itu mungkin bisa dilakukan dengan menggunakan SQL dinamis bersarang berbelit-belit, yang akan menyenangkan untuk debug. Saya akan meninggalkan itu sebagai latihan untuk pembaca.

Saya menggunakan sql_variantasumsi bahwa karena alasan kode keseragaman, prosedur yang sama akan digunakan di mana-mana, bahkan untuk jenis nilai yang sedang didukung langsung oleh RAISERROR. Juga, ini dapat dibuat sebagai prosedur tersimpan sementara jika itu sesuai.

Beginilah tampilan dari prosedur ini:

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;

Keluaran:

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)

Jadi hasil akhirnya adalah Anda tidak mendapatkan kemampuan format untuk float (roll sendiri), tetapi Anda memang mendapatkan kemampuan untuk menghasilkannya (desimal / numerik, juga!) Sambil tetap mempertahankan kemampuan format untuk tipe lainnya.

Jon Seigel
sumber
Wow, itu luar biasa! Saya telah mempertimbangkan untuk melakukan sesuatu seperti ini, tetapi tidak menyadarinya sql_variant, jadi saya terjebak pada daftar argumen dan menganggap itu tidak mungkin. Anda telah mengajari saya sesuatu yang sangat berguna hari ini. Terima kasih banyak!
kmote
@kmote: Tidak masalah; senang saya bisa membantu.
Jon Seigel