2010-01-18 52 views
1

我在SQL2000上安全地將varchar轉換爲int存在一些問題。SQL2000安全地將VARCHAR(256)轉換成INT

我的問題的第一部分是,IsNumeric returns false positives如果你只尋找整數。我知道爲什麼IsNumeric做到這一點雖然(浮動,錢等等也是數字),所以我在谷歌尋找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()返回沒有誤報)和鑄造錯誤會導致整批即使在失敗UDF的根據this website

當在UDF中發生錯誤, 立即中止執行該功能 ,查詢和 也會立即中止,除非該錯誤是中止 批處理的錯誤,執行繼續執行 next語句 - 但@@ error爲0!

即使他們不會仍然隱晦@@error。我也不能投到bigint,因爲它可能仍然會崩潰(儘管不是很常見),而且這個查詢是輸出到XML的UNION的一部分,它被進一步驗證並通過VB6 COM DLL用XSLT轉換並顯示在網站上編碼回到2001年,所以我真的(沒有真的)不想改變查詢輸出!

因此,這讓我停留在這個看似簡單的任務:

如果VARCHAR是澆注料爲int強制轉換爲int,否則給我NULL

任何指針/解決方案將大大apreciated但請注意,在任何情況下,我不能在輸入數據時更改源列的數據類型,也不能更改驗證。

回答

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 
+0

如果mycolumn超過bigint,這仍然會導致批量中止錯誤。 (如我的問題所述)。 – 2010-01-18 19:57:44

+0

經過更新處理,最多可處理37位數字。更重要的是,你不能在SQL – gbn 2010-01-18 20:07:32

+0

謝謝!一直盯着這個太久想清楚了。鑄造到小數似乎更放鬆,然後鑄造爲int。 – 2010-01-18 21:38:07

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 

我已經沒有機會測試,但我認爲它應該工作 - 我已經離開它作爲冗長可能

+0

Apreciated,不是太熱衷於原始的IsNumeric實現,儘管開始:) – 2010-01-18 21:53:27