SQL2000 безопасно приведёт VARCHAR (256) к INT - PullRequest
1 голос
/ 18 января 2010

У меня возникли проблемы с безопасным приведением varchar к int в SQL2000.

Часть 1 моей проблемы заключалась в том, что IsNumeric возвращает ложные срабатывания , если вы ищете целые числатолько.Однако я знаю, почему 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];

Как бы то ни было,t проверить, если целое число находится в пределах диапазона (-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<br> The conversion of the nvarchar value '2147483648' overflowed an int column. Maximum integer value exceeded.

SQL2000 не имеет TRY / CATCH (ответ предполагает, что ISNUMERIC () не возвращает ложных срабатываний), а ошибки приведения приводят к сбою всей партии даже в пределах UDF согласно этому веб-сайту :

При возникновении ошибкив UDF выполнение функции немедленно прерывается, как и запрос, и, если только ошибка не приводит к прерыванию пакета, выполнение продолжается для следующего оператора - но @@ error = 0!

и даже если бы они этого не делали, все равно оставалось неясным @@error.Я также не могу привести к bigint, поскольку он все еще может аварийно завершить работу (хотя и не так часто), и этот запрос является частью UNION, который выводится в XML, который дополнительно проверяется и преобразуется с помощью XSLT с помощью COM-библиотеки VB6 и отображается насайт закодирован еще в 2001 году, поэтому я действительно (не действительно ) не хочу менять вывод запроса!.

Так что я застрял в этой, казалось бы, простой задаче:

если varchar преобразуется в int для int, в противном случае вы получите NULL

Любые указатели / решения будут высоко цениться, но учтите, что я ни при каких обстоятельствах не могу изменитьТип данных исходного столбца также не изменяет проверку при вводе данных.

Ответы [ 2 ]

4 голосов
/ 18 января 2010

Редактировать:

Вы не можете иметь числа свыше десятичные (38,0) в 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

Соблюдайте эту статью за трюк .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
0 голосов
/ 18 января 2010

Вы можете просто добавить еще пару проверок в функцию:

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 

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

...