如何在SQL Server中返回动态十进制数据类型?

时间:2018-09-13 13:04:58

标签: sql sql-server

我试图在下面的函数中添加两个参数,并将结果返回为DECIMAL(@ p1,@ p2),但是我遇到了语法错误-我该怎么做?

/*
---------------------------------------------------------------------------------
SELECT [dbo].fn_ConvertTextToDecimal_19_4('', 0) -- NULL
SELECT [dbo].fn_ConvertTextToDecimal_19_4('', 1) -- 0.0000
SELECT [dbo].fn_ConvertTextToDecimal_19_4('100', 1) -- 100.0000
SELECT [dbo].fn_ConvertTextToDecimal_19_4('1234567890123456.1234', 0) -- NULL
---------------------------------------------------------------------------------
*/
ALTER FUNCTION [dbo].[fn_ConvertTextToDecimal_19_4]
(
    @Decimal_Number VARCHAR(50),
    @Fail_As_Zero BIT
)
RETURNS DECIMAL(19, 4)
AS
BEGIN
    RETURN 
    (
        CASE WHEN 
                TRY_CAST(@Decimal_Number AS DECIMAL(19, 4)) IS NULL
            THEN
                CASE WHEN (@Fail_As_Zero = 1)
                    THEN 0
                    ELSE NULL
                END
            ELSE 
                TRY_CAST(@Decimal_Number AS DECIMAL(19, 4))
        END
    )
END

更新 -在我发现它需要与SQL Server 2008兼容之后,我添加了这个宝贝:

/*
---------------------------------------------------------------------------------
PURPOSE     : Convert a string into decimal(19,4)
    (this is a workaround for SQL server 2008 where TRY_CAST cannot be used)
    based on https://stackoverflow.com/questions/11089125/varchar-to-decimal
    otherwise it can be casted as follows:
    ISNULL(TRY_CAST([column_name] AS DECIMAL(19, 4)), 0) AS [Test_1]
    ISNULL(TRY_CAST([column_name] AS DECIMAL(19, 4)), NULL) AS [Test_2]
NOTE        : for different precision, change the constants in the code (19, 4, 16)
USAGE       :
SELECT [dbo].fn_ConvertTextToDecimal_19_4('', 0) -- NULL
SELECT [dbo].fn_ConvertTextToDecimal_19_4('', 1) -- 0.0000
SELECT [dbo].fn_ConvertTextToDecimal_19_4('100', 1) -- 100.0000
SELECT [dbo].fn_ConvertTextToDecimal_19_4('-123456789012345.1234', 0) -- -123456789012345.1234
---------------------------------------------------------------------------------
*/
CREATE FUNCTION [dbo].[fn_ConvertTextToDecimal_19_4]
(
    @Decimal_Number VARCHAR(50),
    @Fail_As_Zero BIT
)
RETURNS DECIMAL(19, 4)
AS
BEGIN
    RETURN 
    (
        CASE WHEN ISNUMERIC(@Decimal_Number) = 1
                AND CHARINDEX('.', @Decimal_Number) = 0
                AND LEN(REPLACE(REPLACE(@Decimal_Number, '-', ''), '+', '')) < 16
                THEN CONVERT(DECIMAL(19, 4), @Decimal_Number)
            WHEN ISNUMERIC(@Decimal_Number) = 1
                AND (CHARINDEX('.', @Decimal_Number) !=0
                    AND CHARINDEX('.', REPLACE(REPLACE(@Decimal_Number, '-', ''), '+', '')) <= 16)
                THEN CONVERT(DECIMAL(19, 4), 
                    CASE WHEN LEN(@Decimal_Number) - LEN(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@Decimal_Number, '0', ''), '1', ''), '2', ''), '3', ''), '4', ''), '5', ''), '6', ''), '7', ''), '8', ''), '9', '')) <= 19 
                        THEN @Decimal_Number 
                        ELSE SUBSTRING(@Decimal_Number, 1, 19 + LEN(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@Decimal_Number, '0', ''), '1', ''), '2', ''), '3', ''), '4', ''), '5', ''), '6', ''), '7', ''), '8', ''), '9', ''))) 
                    END
                )
            ELSE
                CASE WHEN (@Fail_As_Zero = 1)
                    THEN 0
                    ELSE NULL
                END
            END
    )
END
GO

幻数是:(38,10,29)(19,6,14)(19,4,16)

1 个答案:

答案 0 :(得分:2)

您不能在SQL Server中(以合理的方式)执行此操作。问题?您不能将精度和长度作为参数传递给decimal

“显而易见的”解决方案是使用动态SQL。 las,那是行不通的,因为函数无法调用动态SQL(虽然有一种方法,但是它涉及太多的开销,您不妨嘲笑这种解决方法)。

另一种解决方案将是一个非常有趣的case表达式:

(case when @p = 1 and @s = 1 then try_cast(@input as decimal(1, 1))
      . . .

大约只有38 * 38/2 = 722个条件(精度不能小于刻度)。

我不确定您为什么真正关心数字的精度和小数位数。为什么不只使用str()format()并转换为具有所需表示形式的字符串呢?

编辑:

写完所有这些之后,我意识到该函数必须返回单个类型。您无法在返回值上参数化小数位数和精度,因此SQL Server根本不支持您要执行的操作。