SQL2000安全地将VARCHAR(256)转换为INT

时间:2010-01-18 19:27:09

标签: sql-server-2000 types

我在SQL2000上安全地将varchar转换为int时遇到了一些问题。

我的问题的第1部分是IsNumeric returns false positives,如果你只寻找整数。我知道为什么IsNumeric会这样做(浮点数,钱等也是数字)所以我在google上寻找IsInteger函数。

我找到了以下用户定义函数(UDF):

CREATE FUNCTION dbo.IsInteger
(  
    @num VARCHAR(64)  
)  
RETURNS BIT  
BEGIN  
    IF LEFT(@num, 1) = '-'  
        SET @num = SUBSTRING(@num, 2, LEN(@num))  

    RETURN CASE  
    WHEN PATINDEX('%[^0-9-]%', @num) = 0  
        AND CHARINDEX('-', @num) <= 1  
        AND @num NOT IN ('.', '-', '+', '^') 
        AND LEN(@num)>0  
        AND @num NOT LIKE '%-%' 
    THEN  
        1  
    ELSE  
        0  
    END  
END

这似乎可以很好地检查整数:

declare @num varchar(256);
declare @num2 varchar(256);
set @num = '22312311';
set @num2 = '22312311.0';
SELECT  @num AS [character], 
 dbo.IsInteger(@num) AS [isInteger], 
 CASE dbo.IsInteger(@num)WHEN 1 THEN convert(int, @num) ELSE NULL END AS [integer]
UNION
SELECT  @num2 AS [character], 
 dbo.IsInteger(@num2) AS [isInteger], 
 CASE dbo.IsInteger(@num2)WHEN 1 THEN convert(int, @num2) ELSE NULL END AS [integer];

然而,它不会验证整数是否在范围(-2^31 <=> 2^31 - 1

declare @num varchar(256);
set @num = '2147483648';
SELECT  @num AS [character], 
 dbo.IsInteger(@num) AS [isInteger], 
 CASE dbo.IsInteger(@num)WHEN 1 THEN convert(int, @num) ELSE NULL END AS [integer];

哪个投掷

Server: Msg 248, Level 16, State 1, Line 3
The conversion of the nvarchar value '2147483648' overflowed an int column. Maximum integer value exceeded.

SQL2000 doesn't have TRY/CATCH(回答假设ISNUMERIC()不返回误报)并且根据this website,即使在UDF中,整个批处理也会导致整个批处理失败:

  

当UDF发生错误时,   该函数的执行被中止   立即,所以查询,和   除非错误是中止的错误   批处理,继续执行   下一个陈述 - 但是@@ error是0!

即使他们不这样做仍然会模糊@@error。我也无法转换为bigint,因为它可能仍会崩溃(虽然不常见),并且此查询是输出到XML的UNION的一部分,它通过VB6 COM DLL进一步验证并使用XSLT进行转换,显示在2001年编码的网站上,所以我真的(没有真的)不想更改查询输出!。

所以这让我陷入了这个看似简单的任务:

如果varchar可以转换为int cast to int,否则给我NULL

任何指针/解决方案都会非常复杂,但请注意,在任何情况下,我都不能更改源列的数据类型,也不能在输入数据时更改验证。

2 个答案:

答案 0 :(得分:4)

编辑:

您的数字不能超过decimal(38,0) in SQL Server(+/- 10 ^ 38 -1),因此无法捕获或转换它们。这意味着37个字符可能长度,CAST到十进制(38,0)

SELECT
    CASE
        WHEN CAST(MyColumn AS decimal(38,0) BETWEEN -2147483648 AND 2147483647 THEN  CAST(MyColumn AS int)
        ELSE NULL
    END
FROM
    MyTable
WHERE
    ISNUMERIC(MyColumn + '.0e0') = 1 AND LEN(MyColumn) <= 37

尊重this article .0e0技巧

编辑OP
这个问题引导我进入下面更新的IsInteger函数。

CREATE FUNCTION dbo.IsInteger
(  
    @num VARCHAR(256)  
)  
RETURNS BIT
BEGIN    
    RETURN CASE 
            WHEN ISNUMERIC(@num + '.0e0') = 1  AND convert(decimal(38,0), @num) BETWEEN -2147483648 AND 2147483647 THEN  1
            ELSE 0
    END
END

答案 1 :(得分:0)

您可以在函数中添加几个检查:

CREATE FUNCTION [dbo].[IsInteger] 
(   
    @num VARCHAR(64)   
)   
RETURNS BIT   
BEGIN   
    IF LEFT(@num, 1) = '-'   
        SET @num = SUBSTRING(@num, 2, LEN(@num))   

    DECLARE @IsInt BIT

    SELECT @IsInt = CASE   
    WHEN PATINDEX('%[^0-9-]%', @num) = 0   
        AND CHARINDEX('-', @num) <= 1   
        AND @num NOT IN ('.', '-', '+', '^')  
        AND LEN(@num)>0   
        AND @num NOT LIKE '%-%'  
    THEN   
         1
    ELSE   
         0
    END   

    IF @IsInt = 1
        BEGIN

            IF LEN(@num) <= 11
                BEGIN
                    DECLARE @test bigint
                    SELECT @test = convert(bigint, @num)
                    IF @test <= 2147483647 AND @test >= -2147483648
                        BEGIN
                            set @IsInt = 1
                        END
                    ELSE
                        BEGIN
                            set @IsInt = 0
                        END
                END
            ELSE
                BEGIN
                    set @IsInt = 0
                END
        END


    RETURN @IsInt

END 

我没有机会参加测试,但我认为它应该可行 - 我已经尽可能详细地将其保留了