TSQL - 将字符串转换为整数或返回默认值

Oli*_*ppi 111 sql t-sql

在转换失败的情况下,T-SQL中是否有一种方法可以将nvarchar转换为int并返回默认值或NULL?

Grz*_*lik 130

是的:).试试这个:

DECLARE @text AS NVARCHAR(10)

SET @text = '100'
SELECT CASE WHEN ISNUMERIC(@text) = 1 THEN CAST(@text AS INT) ELSE NULL END
-- returns 100

SET @text = 'XXX'
SELECT CASE WHEN ISNUMERIC(@text) = 1 THEN CAST(@text AS INT) ELSE NULL END
-- returns NULL
Run Code Online (Sandbox Code Playgroud)

ISNUMERIC()有一些问题由Fedor Hajdu指出.

喜欢它返回的字符串真$(是货币),,.(两者都是分隔符),+-.

  • -1:不幸的是,"ISNUMERIC"有太多问题,因为这个答案对未经验证的数据有任何用处(并且你不需要首先检查`ISNUMERIC`以获得经过适当验证的数据).作者承认存在这些问题,但没有解决这些问题. (7认同)
  • 正如[Joseph Mentions](http://stackoverflow.com/a/11552624/18192)所述,SQL 2012+的用户应该使用[`TRY_CONVERT`](https://msdn.microsoft.com/en-us/library/ hh230993.aspx)/ [`TRY_CAST`](https://msdn.microsoft.com/en-us/library/hh974669.aspx),明确旨在解决OP的问题.这个答案是在SQL 2012发布之前编写的. (6认同)
  • 当文本值是数字但包含大于int列的数字可以存储时呢? (4认同)
  • 感谢您的回答.我希望有更优雅的东西. (3认同)
  • 我是downvote,因为ISNUMERIC('1,1,1,1,1')= 1并且它永远不会转换为int. (3认同)

Jos*_*ant 62

如果您使用的是SQL Server 2012(或更新版本):

使用TRY_CONVERT函数.

如果您使用的是SQL Server 2005,2008或2008 R2:

创建用户定义的函数.这将避免Fedor Hajdu在货币,分数等方面提到的问题:

CREATE FUNCTION dbo.TryConvertInt(@Value varchar(18))
RETURNS int
AS
BEGIN
    SET @Value = REPLACE(@Value, ',', '')
    IF ISNUMERIC(@Value + 'e0') = 0 RETURN NULL
    IF ( CHARINDEX('.', @Value) > 0 AND CONVERT(bigint, PARSENAME(@Value, 1)) <> 0 ) RETURN NULL
    DECLARE @I bigint =
        CASE
        WHEN CHARINDEX('.', @Value) > 0 THEN CONVERT(bigint, PARSENAME(@Value, 2))
        ELSE CONVERT(bigint, @Value)
        END
    IF ABS(@I) > 2147483647 RETURN NULL
    RETURN @I
END
GO

-- Testing
DECLARE @Test TABLE(Value nvarchar(50))    -- Result
INSERT INTO @Test SELECT '1234'            -- 1234
INSERT INTO @Test SELECT '1,234'           -- 1234
INSERT INTO @Test SELECT '1234.0'          -- 1234
INSERT INTO @Test SELECT '-1234'           -- -1234
INSERT INTO @Test SELECT '$1234'           -- NULL
INSERT INTO @Test SELECT '1234e10'         -- NULL
INSERT INTO @Test SELECT '1234 5678'       -- NULL
INSERT INTO @Test SELECT '123-456'         -- NULL
INSERT INTO @Test SELECT '1234.5'          -- NULL
INSERT INTO @Test SELECT '123456789000000' -- NULL
INSERT INTO @Test SELECT 'N/A'             -- NULL
SELECT Value, dbo.TryConvertInt(Value) FROM @Test
Run Code Online (Sandbox Code Playgroud)

参考:我在创建解决方案时广泛使用了此页面.

  • @KeithHoffman对我很有用!+1约瑟夫,谢谢! (3认同)
  • 仍然适合我.你遇到了什么错误? (2认同)

Fed*_*jdu 14

我宁愿创建一个像TryParse这样的函数,也可以使用T-SQL TRY-CATCH块来获得你想要的东西.

ISNUMERIC并不总是按预期工作.如果你这样做,之前给出的代码将失败:

SET @text ='$'

$ sign可以转换为money数据类型,因此ISNUMERIC()在这种情况下返回true.它将对' - '(减号),','(逗号)和'.'执行相同的操作.字符.

  • True :(.``ISNUMERIC()`也为`,`和`.`返回`1`. (3认同)
  • ...除了你不能在函数内使用TRY-CATCH(至少不在我的SQL 2005数据库中)... [link](http://connect.microsoft.com/SQLServer/feedback/details/132225/try-追赶失败 - 内 - 一个功能) (3认同)

Dou*_*las 11

如前所述,如果您使用ISNUMERIC以下内容,可能会遇到几个问题:

-- Incorrectly gives 0:
SELECT CASE WHEN ISNUMERIC('-') = 1 THEN CAST('-' AS INT) END   

-- Error (conversion failure):
SELECT CASE WHEN ISNUMERIC('$') = 1 THEN CAST('$' AS INT) END
SELECT CASE WHEN ISNUMERIC('4.4') = 1 THEN CAST('4.4' AS INT) END
SELECT CASE WHEN ISNUMERIC('1,300') = 1 THEN CAST('1,300' AS INT) END

-- Error (overflow):
SELECT CASE WHEN ISNUMERIC('9999999999') = 1 THEN CAST('9999999999' AS INT) END
Run Code Online (Sandbox Code Playgroud)

如果您想要可靠的转换,则需要自己编写代码.

更新:我的新建议是使用中间测试转换FLOAT来验证号码.这种方法基于adrianm的评论.逻辑可以定义为内联表值函数:

CREATE FUNCTION TryConvertInt (@text NVARCHAR(MAX)) 
RETURNS TABLE
AS
RETURN
(
    SELECT
        CASE WHEN ISNUMERIC(@text + '.e0') = 1 THEN 
             CASE WHEN CONVERT(FLOAT, @text) BETWEEN -2147483648 AND 2147483647 
                  THEN CONVERT(INT, @text) 
             END 
         END AS [Result]
)
Run Code Online (Sandbox Code Playgroud)

一些测试:

SELECT [Conversion].[Result]
FROM ( VALUES
     ( '1234'                     )   -- 1234
   , ( '1,234'                    )   -- NULL
   , ( '1234.0'                   )   -- NULL
   , ( '-1234'                    )   -- -1234
   , ( '$1234'                    )   -- NULL
   , ( '1234e10'                  )   -- NULL
   , ( '1234 5678'                )   -- NULL
   , ( '123-456'                  )   -- NULL
   , ( '1234.5'                   )   -- NULL
   , ( '123456789000000'          )   -- NULL
   , ( 'N/A'                      )   -- NULL
   , ( '-'                        )   -- NULL
   , ( '$'                        )   -- NULL
   , ( '4.4'                      )   -- NULL
   , ( '1,300'                    )   -- NULL
   , ( '9999999999'               )   -- NULL
   , ( '00000000000000001234'     )   -- 1234
   , ( '212110090000000235698741' )   -- NULL
) AS [Source] ([Text])
OUTER APPLY TryConvertInt ([Source].[Text]) AS [Conversion]
Run Code Online (Sandbox Code Playgroud)

结果类似于Joseph Sturtevant的答案,主要区别如下:

  • 我的逻辑不容忍出现.,为了模仿原生INT转换的行为.'1,234''1234.0'返回NULL.
  • 由于它不使用局部变量,我的函数可以定义为内联表值函数,允许更好的查询优化.
  • 由于论证的无声截断,约瑟夫的答案可能导致错误的结果; '00000000000000001234'评估为12.增加参数长度会导致溢出的数字出错BIGINT,例如BBAN(基本银行帐号)'212110090000000235698741'.

撤回:不再推荐以下方法,仅供参考.

下面的代码段适用于非负整数.它检查您的字符串是否包含任何非数字字符,不为空,并且不会溢出(超过该int类型的最大值).但是,NULL由于前导零,它还提供长度超过10个字符的有效整数.

SELECT 
    CASE WHEN @text NOT LIKE '%[^0-9]%' THEN
         CASE WHEN LEN(@text) BETWEEN 1 AND 9 
                OR LEN(@text) = 10 AND @text <= '2147483647' 
              THEN CAST (@text AS INT)
         END
    END 
Run Code Online (Sandbox Code Playgroud)

如果要支持任意数量的前导零,请使用以下内容.嵌套CASE语句虽然不实用,但仍需要促进短路评估并降低错误的可能性(例如,由于将负长度传递给LEFT).

SELECT 
    CASE WHEN @text NOT LIKE '%[^0-9]%' THEN
         CASE WHEN LEN(@text) BETWEEN 1 AND 9 THEN CAST (@text AS INT)
              WHEN LEN(@text) >= 10 THEN
              CASE WHEN LEFT(@text, LEN(@text) - 10) NOT LIKE '%[^0]%'
                    AND RIGHT(@text, 10) <= '2147483647'
                   THEN CAST (@text AS INT)
              END
         END
    END
Run Code Online (Sandbox Code Playgroud)

如果你想支持正负整数与任意数量的前导零的:

SELECT 
         -- Positive integers (or 0):
    CASE WHEN @text NOT LIKE '%[^0-9]%' THEN
         CASE WHEN LEN(@text) BETWEEN 1 AND 9 THEN CAST (@text AS INT)
              WHEN LEN(@text) >= 10 THEN
              CASE WHEN LEFT(@text, LEN(@text) - 10) NOT LIKE '%[^0]%'
                    AND RIGHT(@text, 10) <= '2147483647'
                   THEN CAST (@text AS INT)
              END
         END
         -- Negative integers:
         WHEN LEFT(@text, 1) = '-' THEN
         CASE WHEN RIGHT(@text, LEN(@text) - 1) NOT LIKE '%[^0-9]%' THEN
              CASE WHEN LEN(@text) BETWEEN 2 AND 10 THEN CAST (@text AS INT)
                   WHEN LEN(@text) >= 11 THEN
                   CASE WHEN SUBSTRING(@text, 2, LEN(@text) - 11) NOT LIKE '%[^0]%'
                         AND RIGHT(@text, 10) <= '2147483648'
                        THEN CAST (@text AS INT)
                   END
              END
         END
    END
Run Code Online (Sandbox Code Playgroud)

  • 为了强调这一点,短路是[不保证](http://blogs.msdn.com/b/bartd/archive/2011/03/03/don-t-depend-on-expression-short-circuiting-in -t-sql-not-even-with-case.aspx),即使使用CASE语句也是如此. (3认同)