TSQL – 将string转换为整数或返回默认值
在T-SQL中有一种方法将nvarchar转换为int并返回一个默认值,或者如果转换失败,则返回NULL?
是的:)。 尝试这个:
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
ISNUMERIC()
有Fedor Hajdu指出的一些问题。
它为$
(是货币) ,
或.
(都是分隔符), +
和-
。
如果您在SQL Server 2012(或更新)上:
使用TRY_CONVERTfunction 。
如果您在SQL Server 2005,2008或2008 R2上:
创build一个用户定义的function。 这将避免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
参考:我创build我的解决scheme时广泛使用了此页面 。
我宁愿创build一个像TryParse一样的函数,或者使用T-SQL TRY-CATCH
块来得到你想要的。
ISNUMERIC并不总是按预期工作。 如果你这样做,以前给出的代码将会失败:
SET @text ='$'
$ sign可以转换为money数据types,所以ISNUMERIC()
在这种情况下返回true。 它会为' – '(减号),','(逗号)和'。'做相同的操作。 字符。
如前所述,如果您使用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
如果你想要一个可靠的转换,你需要自己编码。
更新 :我的新build议是将中间testing转换为FLOAT
来validation号码。 这种方法是基于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] )
一些testing:
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]
结果与Joseph Sturtevant的答案类似,主要区别如下:
- 我的逻辑不容忍事件的发生
.
或者为了模仿本地INT
转换的行为。'1,234'
和'1234.0'
返回NULL
。 - 由于它不使用局部variables,所以我的函数可以定义为内联表值函数,从而实现更好的查询优化。
- 约瑟夫的回答可能会导致错误的结果,因为无声的截断了论点;
'00000000000000001234'
计算结果为12
。 增加参数长度将导致溢出BIGINT
数字出错,如BBAN (基本银行帐号),如'212110090000000235698741'
。
撤回 :不再推荐以下方法,仅供参考。
下面的代码片段适用于非负整数。 它检查你的string不包含任何非数字字符,不为空,并且不会溢出(通过超出int
types的最大值)。 但是,由于前导零,长度超过10个字符的有效整数也会产生NULL
。
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
如果你想支持任意数量的前导零,使用下面的。 嵌套的CASE
语句虽然笨拙,但却需要促进短路评估并减less错误发生的可能性(例如,由于负长度而导致出现错误)。
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
如果你想支持任何数量的前导零的正整数和负整数:
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
约瑟夫的答复指出,ISNUMERIC也处理诸如“1.3e + 3”这样的科学记数法,但他的回答并不处理这种数字格式。
铸造钱或浮动首先处理货币和科学问题:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TryConvertInt]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION [dbo].[TryConvertInt] GO CREATE FUNCTION dbo.TryConvertInt(@Value varchar(18)) RETURNS bigint AS BEGIN DECLARE @IntValue bigint; IF (ISNUMERIC(@Value) = 1) IF (@Value like '%e%') SET @IntValue = CAST(Cast(@Value as float) as bigint); ELSE SET @IntValue = CAST(CAST(@Value as money) as bigint); ELSE SET @IntValue = NULL; RETURN @IntValue; END
如果该数字大于bigint,则该函数将失败。
如果你想返回一个不同的默认值,那么保持这个函数的通用性,然后replacenull:
SELECT IsNull(dbo.TryConvertInt('nan') , 1000);
我知道这不是很漂亮,但很简单。 尝试这个:
declare @AlpaNumber nvarchar(50) = 'ABC' declare @MyNumber int = 0 begin Try select @MyNumber = case when ISNUMERIC(@AlpaNumber) = 1 then cast(@AlpaNumber as int) else 0 end End Try Begin Catch -- Do nothing End Catch if exists(select * from mytable where mynumber = @MyNumber) Begin print 'Found' End Else Begin print 'Not Found' End
我对这个问题的解决scheme是创build如下所示的function。 我的要求包括数字必须是一个标准的整数,而不是一个BIGINT,我需要允许负数和正数。 我没有发现这种情况失败的情况。
CREATE FUNCTION [dbo].[udfIsInteger] ( -- Add the parameters for the function here @Value nvarchar(max) ) RETURNS int AS BEGIN -- Declare the return variable here DECLARE @Result int = 0 -- Add the T-SQL statements to compute the return value here DECLARE @MinValue nvarchar(11) = '-2147483648' DECLARE @MaxValue nvarchar(10) = '2147483647' SET @Value = ISNULL(@Value,'') IF LEN(@Value)=0 OR ISNUMERIC(@Value)<>1 OR (LEFT(@Value,1)='-' AND LEN(@Value)>11) OR (LEFT(@Value,1)='-' AND LEN(@Value)=11 AND @Value>@MinValue) OR (LEFT(@Value,1)<>'-' AND LEN(@Value)>10) OR (LEFT(@Value,1)<>'-' AND LEN(@Value)=10 AND @Value>@MaxValue) GOTO FINISHED DECLARE @cnt int = 0 WHILE @cnt<LEN(@Value) BEGIN SET @cnt=@cnt+1 IF SUBSTRING(@Value,@cnt,1) NOT IN ('-','0','1','2','3','4','5','6','7','8','9') GOTO FINISHED END SET @Result=1 FINISHED: -- Return the result of the function RETURN @Result END
问候。
我写了一个有用的标量函数来模拟SQL Server 2008中SQL SERVER 2012的TRY_CAST函数。
你可以在下面的链接中看到它,我们互相帮助改善它。 SQL Server 2008的TRY_CAST函数 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb
两个主要区别是,您必须传递3个参数,并且您必须另外对该字段执行显式的CONVERT或CAST。 但是,它仍然非常有用,因为如果CAST未正确执行,它允许您返回默认值。
dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)
例:
SELECT CASE WHEN dbo.TRY_CAST('6666666166666212', 'INT', DEFAULT) IS NULL THEN 'Cast failed' ELSE 'Cast succeeded' END AS Result;
目前仅支持数据typesINT,DATE,NUMERIC,BIT和FLOAT
希望对你有帮助。
码:
DECLARE @strSQL NVARCHAR(1000) IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[TRY_CAST]')) BEGIN SET @strSQL = 'CREATE FUNCTION [dbo].[TRY_CAST] () RETURNS INT AS BEGIN RETURN 0 END' EXEC sys.sp_executesql @strSQL END SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO /* ------------------------------------------------------------------------------------------------------------------------ Description: Syntax --------------- dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast) +---------------------------+-----------------------+ | Expression | VARCHAR(8000) | +---------------------------+-----------------------+ | Data_Type | VARCHAR(8000) | +---------------------------+-----------------------+ | ReturnValueIfErrorCast | SQL_VARIANT = NULL | +---------------------------+-----------------------+ Arguments --------------- expression The value to be cast. Any valid expression. Data_Type The data type into which to cast expression. ReturnValueIfErrorCast Value returned if cast fails or is not supported. Required. Set the DEFAULT value by default. Return Type ---------------- Returns value cast to SQL_VARIANT type if the cast succeeds; otherwise, returns null if the parameter @pReturnValueIfErrorCast is set to DEFAULT, or that the user indicates. Remarks ---------------- dbo.TRY_CAST function simulates the TRY_CAST function reserved of SQL SERVER 2012 for using in SQL SERVER 2008. dbo.TRY_CAST function takes the value passed to it and tries to convert it to the specified Data_Type. If the cast succeeds, dbo.TRY_CAST returns the value as SQL_VARIANT type; if the cast doesn´t succees, null is returned if the parameter @pReturnValueIfErrorCast is set to DEFAULT. If the Data_Type is unsupported will return @pReturnValueIfErrorCast. dbo.TRY_CAST function requires user make an explicit CAST or CONVERT in ANY statements. This version of dbo.TRY_CAST only supports CAST for INT, DATE, NUMERIC and BIT types. Examples ==================================================================================================== --A. Test TRY_CAST function returns null SELECT CASE WHEN dbo.TRY_CAST('6666666166666212', 'INT', DEFAULT) IS NULL THEN 'Cast failed' ELSE 'Cast succeeded' END AS Result; GO --B. Error Cast With User Value SELECT dbo.TRY_CAST('2147483648', 'INT', DEFAULT) AS [Error Cast With DEFAULT], dbo.TRY_CAST('2147483648', 'INT', -1) AS [Error Cast With User Value], dbo.TRY_CAST('2147483648', 'INT', NULL) AS [Error Cast With User NULL Value]; GO --C. Additional CAST or CONVERT required in any assignment statement DECLARE @IntegerVariable AS INT SET @IntegerVariable = CAST(dbo.TRY_CAST(123, 'INT', DEFAULT) AS INT) SELECT @IntegerVariable GO IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp CREATE TABLE #temp ( Id INT IDENTITY , FieldNumeric NUMERIC(3, 1) ) INSERT INTO dbo.#temp (FieldNumeric) SELECT CAST(dbo.TRY_CAST(12.3, 'NUMERIC(3,1)', 0) AS NUMERIC(3, 1));--Need explicit CAST on INSERT statements SELECT * FROM #temp DROP TABLE #temp GO --D. Supports CAST for INT, DATE, NUMERIC and BIT types. SELECT dbo.TRY_CAST(2147483648, 'INT', 0) AS [Cast failed] , dbo.TRY_CAST(2147483647, 'INT', 0) AS [Cast succeeded] , SQL_VARIANT_PROPERTY(dbo.TRY_CAST(212, 'INT', 0), 'BaseType') AS [BaseType]; SELECT dbo.TRY_CAST('AAAA0101', 'DATE', DEFAULT) AS [Cast failed] , dbo.TRY_CAST('20160101', 'DATE', DEFAULT) AS [Cast succeeded] , SQL_VARIANT_PROPERTY(dbo.TRY_CAST('2016-01-01', 'DATE', DEFAULT), 'BaseType') AS [BaseType]; SELECT dbo.TRY_CAST(1.23, 'NUMERIC(3,1)', DEFAULT) AS [Cast failed] , dbo.TRY_CAST(12.3, 'NUMERIC(3,1)', DEFAULT) AS [Cast succeeded] , SQL_VARIANT_PROPERTY(dbo.TRY_CAST(12.3, 'NUMERIC(3,1)', DEFAULT), 'BaseType') AS [BaseType]; SELECT dbo.TRY_CAST('A', 'BIT', DEFAULT) AS [Cast failed] , dbo.TRY_CAST(1, 'BIT', DEFAULT) AS [Cast succeeded] , SQL_VARIANT_PROPERTY(dbo.TRY_CAST('123', 'BIT', DEFAULT), 'BaseType') AS [BaseType]; GO --EB TRY_CAST return NULL on unsupported data_types SELECT dbo.TRY_CAST(4, 'xml', DEFAULT) AS [unsupported]; GO ==================================================================================================== ------------------------------------------------------------------------------------------------------------------------ Responsible: Javier Pardo Date: diciembre 29/2016 WB tests: Javier Pardo ------------------------------------------------------------------------------------------------------------------------ Update by: Javier Eduardo Pardo Moreno Date: febrero 16/2017 Id update: JEPM20170216 Description: Fix ISNUMERIC function makes it unreliable. SELECT dbo.TRY_CAST('+', 'INT', 0) will yield Msg 8114, Level 16, State 5, Line 16 Error converting data type varchar to float. ISNUMERIC() function treats few more characters as numeric, like: – (minus), + (plus), $ (dollar), \ (back slash), (.)dot and (,)comma Collaborator aperiooculus (http://stackoverflow.com/users/3083382/aperiooculus ) Fix dbo.TRY_CAST('2013/09/20', 'datetime', DEFAULT) for supporting DATETIME format WB tests: Javier Pardo ------------------------------------------------------------------------------------------------------------------------ */ ALTER FUNCTION dbo.TRY_CAST ( @pExpression AS VARCHAR(8000), @pData_Type AS VARCHAR(8000), @pReturnValueIfErrorCast AS SQL_VARIANT = NULL ) RETURNS SQL_VARIANT AS BEGIN -------------------------------------------------------------------------------- -- INT -------------------------------------------------------------------------------- IF @pData_Type = 'INT' BEGIN IF ISNUMERIC(@pExpression) = 1 AND @pExpression NOT IN ('-','+','$','.',',','\') --JEPM20170216 BEGIN DECLARE @pExpressionINT AS FLOAT = CAST(@pExpression AS FLOAT) IF @pExpressionINT BETWEEN - 2147483648.0 AND 2147483647.0 BEGIN RETURN CAST(@pExpressionINT as INT) END ELSE BEGIN RETURN @pReturnValueIfErrorCast END --FIN IF @pExpressionINT BETWEEN - 2147483648.0 AND 2147483647.0 END ELSE BEGIN RETURN @pReturnValueIfErrorCast END -- FIN IF ISNUMERIC(@pExpression) = 1 END -- FIN IF @pData_Type = 'INT' -------------------------------------------------------------------------------- -- DATE -------------------------------------------------------------------------------- IF @pData_Type IN ('DATE','DATETIME') BEGIN IF ISDATE(@pExpression) = 1 BEGIN DECLARE @pExpressionDATE AS DATETIME = cast(@pExpression AS DATETIME) IF @pData_Type = 'DATE' BEGIN RETURN cast(@pExpressionDATE as DATE) END IF @pData_Type = 'DATETIME' BEGIN RETURN cast(@pExpressionDATE as DATETIME) END END ELSE BEGIN DECLARE @pExpressionDATEReplaced AS VARCHAR(50) = REPLACE(REPLACE(REPLACE(@pExpression,'\',''),'/',''),'-','') IF ISDATE(@pExpressionDATEReplaced) = 1 BEGIN IF @pData_Type = 'DATE' BEGIN RETURN cast(@pExpressionDATEReplaced as DATE) END IF @pData_Type = 'DATETIME' BEGIN RETURN cast(@pExpressionDATEReplaced as DATETIME) END END ELSE BEGIN RETURN @pReturnValueIfErrorCast END END --FIN IF ISDATE(@pExpression) = 1 END --FIN IF @pData_Type = 'DATE' -------------------------------------------------------------------------------- -- NUMERIC -------------------------------------------------------------------------------- IF @pData_Type LIKE 'NUMERIC%' BEGIN IF ISNUMERIC(@pExpression) = 1 BEGIN DECLARE @TotalDigitsOfType AS INT = SUBSTRING(@pData_Type,CHARINDEX('(',@pData_Type)+1, CHARINDEX(',',@pData_Type) - CHARINDEX('(',@pData_Type) - 1) , @TotalDecimalsOfType AS INT = SUBSTRING(@pData_Type,CHARINDEX(',',@pData_Type)+1, CHARINDEX(')',@pData_Type) - CHARINDEX(',',@pData_Type) - 1) , @TotalDigitsOfValue AS INT , @TotalDecimalsOfValue AS INT , @TotalWholeDigitsOfType AS INT , @TotalWholeDigitsOfValue AS INT SET @pExpression = REPLACE(@pExpression, ',','.') SET @TotalDigitsOfValue = LEN(REPLACE(@pExpression, '.','')) SET @TotalDecimalsOfValue = CASE Charindex('.', @pExpression) WHEN 0 THEN 0 ELSE Len(Cast(Cast(Reverse(CONVERT(VARCHAR(50), @pExpression, 128)) AS FLOAT) AS BIGINT)) END SET @TotalWholeDigitsOfType = @TotalDigitsOfType - @TotalDecimalsOfType SET @TotalWholeDigitsOfValue = @TotalDigitsOfValue - @TotalDecimalsOfValue -- The total digits can not be greater than the p part of NUMERIC (p, s) -- The total of decimals can not be greater than the part s of NUMERIC (p, s) -- The total digits of the whole part can not be greater than the subtraction between p and s IF (@TotalDigitsOfValue <= @TotalDigitsOfType) AND (@TotalDecimalsOfValue <= @TotalDecimalsOfType) AND (@TotalWholeDigitsOfValue <= @TotalWholeDigitsOfType) BEGIN DECLARE @pExpressionNUMERIC AS FLOAT SET @pExpressionNUMERIC = CAST (ROUND(@pExpression, @TotalDecimalsOfValue) AS FLOAT) RETURN @pExpressionNUMERIC --Returns type FLOAT END else BEGIN RETURN @pReturnValueIfErrorCast END-- FIN IF (@TotalDigitisOfValue <= @TotalDigits) AND (@TotalDecimalsOfValue <= @TotalDecimals) END ELSE BEGIN RETURN @pReturnValueIfErrorCast END --FIN IF ISNUMERIC(@pExpression) = 1 END --IF @pData_Type LIKE 'NUMERIC%' -------------------------------------------------------------------------------- -- BIT -------------------------------------------------------------------------------- IF @pData_Type LIKE 'BIT' BEGIN IF ISNUMERIC(@pExpression) = 1 BEGIN RETURN CAST(@pExpression AS BIT) END ELSE BEGIN RETURN @pReturnValueIfErrorCast END --FIN IF ISNUMERIC(@pExpression) = 1 END --IF @pData_Type LIKE 'BIT' -------------------------------------------------------------------------------- -- FLOAT -------------------------------------------------------------------------------- IF @pData_Type LIKE 'FLOAT' BEGIN IF ISNUMERIC(REPLACE(REPLACE(@pExpression, CHAR(13), ''), CHAR(10), '')) = 1 BEGIN RETURN CAST(@pExpression AS FLOAT) END ELSE BEGIN IF REPLACE(@pExpression, CHAR(13), '') = '' --Only white spaces are replaced, not new lines BEGIN RETURN 0 END ELSE BEGIN RETURN @pReturnValueIfErrorCast END --IF REPLACE(@pExpression, CHAR(13), '') = '' END --FIN IF ISNUMERIC(@pExpression) = 1 END --IF @pData_Type LIKE 'FLOAT' -------------------------------------------------------------------------------- -- Any other unsupported data type will return NULL or the value assigned by the user to @pReturnValueIfErrorCast -------------------------------------------------------------------------------- RETURN @pReturnValueIfErrorCast END