Rol*_*son 31 sql sql-server sql-server-2008 sql-server-2012
我正在使用SQL Server 2008 R2并且有一个VARCHAR我想要转换为DECIMAL(28,10)使用的列CONVERT.但是其中许多行格式错误,因此无法将它们解析为数字.在这种情况下,我只想通过将结果设置为0或NULL来跳过这些.
我知道SQL Server 2012(TRY_CONVERT())中有一个新的语句会很方便.
这可能是在2008年还是我必须等到我们更新到下一个版本的SQL 2012?
编辑
不幸的ISNUMERIC()是,在这种情况下不可靠.我试过了
ISNUMERIC(myCol) = 1
Run Code Online (Sandbox Code Playgroud)
对于CONVERT无法转换为的行,返回true DECIMAL.
Mik*_*son 44
在SQL Server中使用XML时,您可以尝试强制转换为数据类型,并在转换失败时接收空值.
declare @T table
(
Col varchar(50)
)
insert into @T values
('1'),
('1.1'),
('1,1'),
('1a')
select cast('' as xml).value('sql:column("Col") cast as xs:decimal ?',
'decimal(28,10)') as Col
from @T
Run Code Online (Sandbox Code Playgroud)
结果:
Col
-------------
1.0000000000
1.1000000000
NULL
NULL
Run Code Online (Sandbox Code Playgroud)
我编写了一个有用的标量函数来模拟 SQL Server 2008 中 SQL SERVER 2012 的 TRY_CAST 函数。
\n\ndbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\nSQL Server 2012 的 TRY_CAST 函数的两个主要区别是您必须传递 3 个参数,并且您必须另外对字段执行显式 CONVERT 或 CAST。但是,它仍然非常有用,因为它允许您返回一个\n 如果 CAST 未正确执行,则使用默认值。
\n
功能代码:
\n\nDECLARE @strSQL NVARCHAR(1000)\nIF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N\'[dbo].[TRY_CAST]\'))\n BEGIN\n SET @strSQL = \'CREATE FUNCTION [dbo].[TRY_CAST] () RETURNS INT AS BEGIN RETURN 0 END\'\n EXEC sys.sp_executesql @strSQL\n END\n\nSET ANSI_NULLS ON\nGO\nSET QUOTED_IDENTIFIER ON\nGO\n\n/*\n------------------------------------------------------------------------------------------------------------------------\n Description: \n Syntax \n ---------------\n dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)\n\n +---------------------------+-----------------------+\n | Expression | VARCHAR(8000) |\n +---------------------------+-----------------------+\n | Data_Type | VARCHAR(8000) |\n +---------------------------+-----------------------+\n | ReturnValueIfErrorCast | SQL_VARIANT = NULL |\n +---------------------------+-----------------------+\n\n\n Arguments\n ---------------\n expression\n The value to be cast. Any valid expression.\n\n Data_Type\n The data type into which to cast expression.\n\n ReturnValueIfErrorCast\n Value returned if cast fails or is not supported. Required. Set the DEFAULT value by default.\n\n\n Return Type\n ----------------\n Returns value cast to SQL_VARIANT type if the cast succeeds; otherwise, returns null if the parameter @pReturnValueIfErrorCast is set to DEFAULT, \n or that the user indicates.\n\n\n Remarks\n ----------------\n dbo.TRY_CAST function simulates the TRY_CAST function reserved of SQL SERVER 2012 for using in SQL SERVER 2008. \n dbo.TRY_CAST function takes the value passed to it and tries to convert it to the specified Data_Type. \n If the cast succeeds, dbo.TRY_CAST returns the value as SQL_VARIANT type; if the cast doesn\xc2\xb4t succees, null is returned if the parameter @pReturnValueIfErrorCast is set to DEFAULT. \n If the Data_Type is unsupported will return @pReturnValueIfErrorCast.\n dbo.TRY_CAST function requires user make an explicit CAST or CONVERT in ANY statements.\n This version of dbo.TRY_CAST only supports CAST for INT, DATE, NUMERIC and BIT types.\n\n\n Examples\n ====================================================================================================\n\n --A. Test TRY_CAST function returns null\n\n SELECT \n CASE WHEN dbo.TRY_CAST(\'6666666166666212\', \'INT\', DEFAULT) IS NULL \n THEN \'Cast failed\' \n ELSE \'Cast succeeded\' \n END AS Result; \n\n GO\n\n --B. Error Cast With User Value\n\n SELECT \n dbo.TRY_CAST(\'2147483648\', \'INT\', DEFAULT) AS [Error Cast With DEFAULT],\n dbo.TRY_CAST(\'2147483648\', \'INT\', -1) AS [Error Cast With User Value],\n dbo.TRY_CAST(\'2147483648\', \'INT\', NULL) AS [Error Cast With User NULL Value]; \n\n GO \n\n --C. Additional CAST or CONVERT required in any assignment statement\n\n DECLARE @IntegerVariable AS INT\n\n SET @IntegerVariable = CAST(dbo.TRY_CAST(123, \'INT\', DEFAULT) AS INT)\n\n SELECT @IntegerVariable\n\n GO \n\n IF OBJECT_ID(\'tempdb..#temp\') IS NOT NULL\n DROP TABLE #temp\n\n CREATE TABLE #temp (\n Id INT IDENTITY\n , FieldNumeric NUMERIC(3, 1)\n )\n\n INSERT INTO dbo.#temp (FieldNumeric)\n SELECT CAST(dbo.TRY_CAST(12.3, \'NUMERIC(3,1)\', 0) AS NUMERIC(3, 1));--Need explicit CAST on INSERT statements\n\n SELECT *\n FROM #temp\n\n DROP TABLE #temp\n\n GO \n\n --D. Supports CAST for INT, DATE, NUMERIC and BIT types.\n\n SELECT dbo.TRY_CAST(2147483648, \'INT\', 0) AS [Cast failed]\n , dbo.TRY_CAST(2147483647, \'INT\', 0) AS [Cast succeeded]\n , SQL_VARIANT_PROPERTY(dbo.TRY_CAST(212, \'INT\', 0), \'BaseType\') AS [BaseType];\n\n SELECT dbo.TRY_CAST(\'AAAA0101\', \'DATE\', DEFAULT) AS [Cast failed]\n , dbo.TRY_CAST(\'20160101\', \'DATE\', DEFAULT) AS [Cast succeeded]\n , SQL_VARIANT_PROPERTY(dbo.TRY_CAST(\'2016-01-01\', \'DATE\', DEFAULT), \'BaseType\') AS [BaseType];\n\n SELECT dbo.TRY_CAST(1.23, \'NUMERIC(3,1)\', DEFAULT) AS [Cast failed]\n , dbo.TRY_CAST(12.3, \'NUMERIC(3,1)\', DEFAULT) AS [Cast succeeded]\n , SQL_VARIANT_PROPERTY(dbo.TRY_CAST(12.3, \'NUMERIC(3,1)\', DEFAULT), \'BaseType\') AS [BaseType];\n\n SELECT dbo.TRY_CAST(\'A\', \'BIT\', DEFAULT) AS [Cast failed]\n , dbo.TRY_CAST(1, \'BIT\', DEFAULT) AS [Cast succeeded]\n , SQL_VARIANT_PROPERTY(dbo.TRY_CAST(\'123\', \'BIT\', DEFAULT), \'BaseType\') AS [BaseType];\n\n GO \n\n --E. B. TRY_CAST return NULL on unsupported data_types\n\n SELECT dbo.TRY_CAST(4, \'xml\', DEFAULT) AS [unsupported]; \n\n GO \n\n ====================================================================================================\n\n------------------------------------------------------------------------------------------------------------------------\n Responsible: Javier Pardo \n Date: diciembre 29/2016\n WB tests: Javier Pardo \n------------------------------------------------------------------------------------------------------------------------\n Update by: Javier Eduardo Pardo Moreno \n Date: febrero 16/2017\n Id update: JEPM20170216\n Description: Fix ISNUMERIC function makes it unreliable. SELECT dbo.TRY_CAST(\'+\', \'INT\', 0) will yield Msg 8114, \n Level 16, State 5, Line 16 Error converting data type varchar to float.\n ISNUMERIC() function treats few more characters as numeric, like: \xe2\x80\x93 (minus), + (plus), $ (dollar), \\ (back slash), (.)dot and (,)comma\n Collaborator aperiooculus (http://stackoverflow.com/users/3083382/aperiooculus )\n\n Fix dbo.TRY_CAST(\'2013/09/20\', \'datetime\', DEFAULT) for supporting DATETIME format\n\n WB tests: Javier Pardo\n\n------------------------------------------------------------------------------------------------------------------------\n*/\n\nALTER FUNCTION dbo.TRY_CAST\n(\n @pExpression AS VARCHAR(8000),\n @pData_Type AS VARCHAR(8000),\n @pReturnValueIfErrorCast AS SQL_VARIANT = NULL\n)\nRETURNS SQL_VARIANT\nAS\nBEGIN\n --------------------------------------------------------------------------------\n -- INT \n --------------------------------------------------------------------------------\n\n IF @pData_Type = \'INT\'\n BEGIN\n IF ISNUMERIC(@pExpression) = 1 AND @pExpression NOT IN (\'-\',\'+\',\'$\',\'.\',\',\',\'\\\') --JEPM20170216\n BEGIN\n DECLARE @pExpressionINT AS FLOAT = CAST(@pExpression AS FLOAT)\n\n IF @pExpressionINT BETWEEN - 2147483648.0 AND 2147483647.0\n BEGIN\n RETURN CAST(@pExpressionINT as INT)\n END\n ELSE\n BEGIN\n RETURN @pReturnValueIfErrorCast\n END --FIN IF @pExpressionINT BETWEEN - 2147483648.0 AND 2147483647.0\n END\n ELSE\n BEGIN\n RETURN @pReturnValueIfErrorCast\n END -- FIN IF ISNUMERIC(@pExpression) = 1\n END -- FIN IF @pData_Type = \'INT\'\n\n --------------------------------------------------------------------------------\n -- DATE \n --------------------------------------------------------------------------------\n\n IF @pData_Type IN (\'DATE\',\'DATETIME\')\n BEGIN\n IF ISDATE(@pExpression) = 1\n BEGIN\n\n DECLARE @pExpressionDATE AS DATETIME = cast(@pExpression AS DATETIME)\n\n IF @pData_Type = \'DATE\'\n BEGIN\n RETURN cast(@pExpressionDATE as DATE)\n END\n\n IF @pData_Type = \'DATETIME\'\n BEGIN\n RETURN cast(@pExpressionDATE as DATETIME)\n END\n\n END\n ELSE \n BEGIN\n\n DECLARE @pExpressionDATEReplaced AS VARCHAR(50) = REPLACE(REPLACE(REPLACE(@pExpression,\'\\\',\'\'),\'/\',\'\'),\'-\',\'\')\n\n IF ISDATE(@pExpressionDATEReplaced) = 1\n BEGIN\n IF @pData_Type = \'DATE\'\n BEGIN\n RETURN cast(@pExpressionDATEReplaced as DATE)\n END\n\n IF @pData_Type = \'DATETIME\'\n BEGIN\n RETURN cast(@pExpressionDATEReplaced as DATETIME)\n END\n\n END\n ELSE\n BEGIN\n RETURN @pReturnValueIfErrorCast\n END\n END --FIN IF ISDATE(@pExpression) = 1\n END --FIN IF @pData_Type = \'DATE\'\n\n --------------------------------------------------------------------------------\n -- NUMERIC \n --------------------------------------------------------------------------------\n\n IF @pData_Type LIKE \'NUMERIC%\'\n BEGIN\n\n IF ISNUMERIC(@pExpression) = 1\n BEGIN\n\n DECLARE @TotalDigitsOfType AS INT = SUBSTRING(@pData_Type,CHARINDEX(\'(\',@pData_Type)+1, CHARINDEX(\',\',@pData_Type) - CHARINDEX(\'(\',@pData_Type) - 1)\n , @TotalDecimalsOfType AS INT = SUBSTRING(@pData_Type,CHARINDEX(\',\',@pData_Type)+1, CHARINDEX(\')\',@pData_Type) - CHARINDEX(\',\',@pData_Type) - 1)\n , @TotalDigitsOfValue AS INT \n , @TotalDecimalsOfValue AS INT \n , @TotalWholeDigitsOfType AS INT \n , @TotalWholeDigitsOfValue AS INT \n\n SET @pExpression = REPLACE(@pExpression, \',\',\'.\')\n\n SET @TotalDigitsOfValue = LEN(REPLACE(@pExpression, \'.\',\'\'))\n SET @TotalDecimalsOfValue = CASE Charindex(\'.\', @pExpression)\n WHEN 0\n THEN 0\n ELSE Len(Cast(Cast(Reverse(CONVERT(VARCHAR(50), @pExpression, 128)) AS FLOAT) AS BIGINT))\n END \n SET @TotalWholeDigitsOfType = @TotalDigitsOfType - @TotalDecimalsOfType\n SET @TotalWholeDigitsOfValue = @TotalDigitsOfValue - @TotalDecimalsOfValue\n\n -- The total digits can not be greater than the p part of NUMERIC (p, s)\n -- The total of decimals can not be greater than the part s of NUMERIC (p, s)\n -- The total digits of the whole part can not be greater than the subtraction between p and s\n IF (@TotalDigitsOfValue <= @TotalDigitsOfType) AND (@TotalDecimalsOfValue <= @TotalDecimalsOfType) AND (@TotalWholeDigitsOfValue <= @TotalWholeDigitsOfType)\n BEGIN\n DECLARE @pExpressionNUMERIC AS FLOAT\n SET @pExpressionNUMERIC = CAST (ROUND(@pExpression, @TotalDecimalsOfValue) AS FLOAT) \n\n RETURN @pExpressionNUMERIC --Returns type FLOAT\n END \n else\n BEGIN\n RETURN @pReturnValueIfErrorCast\n END-- FIN IF (@TotalDigitisOfValue <= @TotalDigits) AND (@TotalDecimalsOfValue <= @TotalDecimals) \n\n END\n ELSE \n BEGIN\n RETURN @pReturnValueIfErrorCast\n END --FIN IF ISNUMERIC(@pExpression) = 1\n END --IF @pData_Type LIKE \'NUMERIC%\'\n\n --------------------------------------------------------------------------------\n -- BIT \n --------------------------------------------------------------------------------\n\n IF @pData_Type LIKE \'BIT\'\n BEGIN\n IF ISNUMERIC(@pExpression) = 1\n BEGIN\n RETURN CAST(@pExpression AS BIT) \n END\n ELSE \n BEGIN\n RETURN @pReturnValueIfErrorCast\n END --FIN IF ISNUMERIC(@pExpression) = 1\n END --IF @pData_Type LIKE \'BIT\'\n\n\n --------------------------------------------------------------------------------\n -- FLOAT \n --------------------------------------------------------------------------------\n\n IF @pData_Type LIKE \'FLOAT\'\n BEGIN\n IF ISNUMERIC(REPLACE(REPLACE(@pExpression, CHAR(13), \'\'), CHAR(10), \'\')) = 1\n BEGIN\n\n RETURN CAST(@pExpression AS FLOAT) \n END\n ELSE \n BEGIN\n\n IF REPLACE(@pExpression, CHAR(13), \'\') = \'\' --Only white spaces are replaced, not new lines\n BEGIN\n RETURN 0\n END\n ELSE \n BEGIN\n RETURN @pReturnValueIfErrorCast\n END --IF REPLACE(@pExpression, CHAR(13), \'\') = \'\' \n\n END --FIN IF ISNUMERIC(@pExpression) = 1\n END --IF @pData_Type LIKE \'FLOAT\'\n\n --------------------------------------------------------------------------------\n -- Any other unsupported data type will return NULL or the value assigned by the user to @pReturnValueIfErrorCast \n --------------------------------------------------------------------------------\n\n RETURN @pReturnValueIfErrorCast\n\n\n\nEND\nRun Code Online (Sandbox Code Playgroud)\n\n目前仅支持数据类型INT、DATE、DATETIME、NUMERIC、BIT 和 FLOAT。您可以在下面的下一个链接中找到此代码的最后一个版本\xc3\xb3n,我们会互相帮助改进它。SQL Server 2008 的 TRY_CAST 函数 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb
\n您可以用 C# 编写自己的自定义解析器并使用 SQLCLR,例如使用Decimal.Parse(). 不要尝试使用ISNUMERIC, 是众所周知的错误(对于 CAST 失败的字符串返回 TRUE)。
由于这是一个永久性的变化,我将把它作为一个两步过程 - 首先,删除无效文本,然后转换列.
要删除无效文本,我会执行以下操作:
UPDATE [Table]
SET [Column] = NULL
WHERE [Column] LIKE '%[^0-9.]%' or
LEN([Column]) - LEN(REPLACE([Column],'.','')) > 1 or
LEN([Column]) > 28
Run Code Online (Sandbox Code Playgroud)
完成之后,只需更改列定义就可以转换剩余的所有内容
ALTER TABLE [Table] ALTER COLUMN [Column] decimal(28,10)
Run Code Online (Sandbox Code Playgroud)
最后在SO和Google的帮助下找到了如何制作它。
更新声明:
UPDATE PriceTerm
SET PercentAddition = CONVERT(decimal(28,10), RTRIM(LTRIM(REPLACE(REPLACE(REPLACE(AdditionalDescription,'%',''), ',','.'), '&', ''))))
WHERE AdditionalDescription LIKE '%[%]%' AND
dbo.isreallynumeric(RTRIM(LTRIM(REPLACE(REPLACE(REPLACE(AdditionalDescription,'%',''), ',','.'), '&', '')))) = 1 AND
PercentAddition = 0
Run Code Online (Sandbox Code Playgroud)
首先,我搜索 % char,因为大多数情况下它用作百分比值的标记。但也有随机的其他用途。事实证明 ISNUMERIC 在我的情况下并不可靠。
真正重要的是从这里对存储过程的调用实际上是数字。
所以
CREATE FUNCTION dbo.isReallyNumeric
(
@num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(@num, 1) = '-'
SET @num = SUBSTRING(@num, 2, LEN(@num))
DECLARE @pos TINYINT
SET @pos = 1 + LEN(@num) - CHARINDEX('.', REVERSE(@num))
RETURN CASE
WHEN PATINDEX('%[^0-9.-]%', @num) = 0
AND @num NOT IN ('.', '-', '+', '^')
AND LEN(@num)>0
AND @num NOT LIKE '%-%'
AND
(
((@pos = LEN(@num)+1)
OR @pos = CHARINDEX('.', @num))
)
THEN
1
ELSE
0
END
END
GO
Run Code Online (Sandbox Code Playgroud)