Try_Convert for SQL Server 2008 R2

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)


Jot*_*rdo 6

我编写了一个有用的标量函数来模拟 SQL Server 2008 中 SQL SERVER 2012 的 TRY_CAST 函数。

\n\n
dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

SQL Server 2012 的 TRY_CAST 函数的两个主要区别是您必须传递 3 个参数,并且您必须另外对字段执行显式 CONVERT 或 CAST。但是,它仍然非常有用,因为它允许您返回一个\n 如果 CAST 未正确执行,则使用默认值。

\n
\n\n

功能代码:

\n\n
DECLARE @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\n
Run 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

  • 您的函数对 ISNUMERIC 函数的依赖使其不可靠。`SELECT dbo.TRY_CAST('+', 'INT', 0)` 将产生 _Msg 8114, Level 16, State 5, Line 16 将数据类型 varchar 转换为 float._ (3认同)
  • @AperioOculus 我感谢你的帮助。我更新了代码!https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb (2认同)

Rem*_*anu 5

您可以用 C# 编写自己的自定义解析器并使用 SQLCLR,例如使用Decimal.Parse(). 不要尝试使用ISNUMERIC, 是众所周知的错误(对于 CAST 失败的字符串返回 TRUE)。

  • `ISNUMERIC` 并不是不正确,它只是回答了一个没有人真正想要答案的问题(“我可以将此字符串转换为*任何*数字数据类型吗?”) (9认同)

Dam*_*ver 5

由于这是一个永久性的变化,我将把它作为一个两步过程 - 首先,删除无效文本,然后转换列.

要删除无效文本,我会执行以下操作:

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)


Rol*_*son 5

最后在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)