简体   繁体   English

SQL Server 2008 R2 Express中的try_convert

[英]try_convert in SQL Server 2008 R2 Express

I have a query that was written for SQL Server 2012, and uses the try_convert() function. 我有一个为SQL Server 2012写的查询,并使用try_convert()函数。 I now need to execute the query on SQL Server 2008 R2 Express, and it is my understanding that try_convert() isn't a supported function in 2008 R2. 现在,我需要在SQL Server 2008 R2 Express上执行查询,据我了解, try_convert()在2008 R2中不受支持。

My current query contains this block: 我当前的查询包含以下块:

CASE WHEN (try_convert(decimal, tew_userdata_locTo.use_data0) IS NULL) 
THEN .. ELSE .. END

This gives me a true or false result letting me know if I can convert the particular value to a decimal. 这给了我一个正确或错误的结果,让我知道我是否可以将特定的值转换为小数。 If I can convert the value, I do so. 如果我可以转换值,则可以。 If not, I use a default decimal value and carry on with that. 如果没有,我将使用默认的十进制值并继续使用该值。

Any ideas on what I could write that would work in SQL Server 2008 R2? 关于我可以写的内容的任何想法都可以在SQL Server 2008 R2中使用吗?

try_convert() attempts to do the convert. try_convert()尝试进行转换。 This is a nice way of handling conversion errors (or at least better than SQL Server previously did, which was nothing). 这是处理转换错误的好方法(或者至少比以前的SQL Server更好,这没什么)。 You can use like to see if the string "looks" like a valid decimal. 您可以使用like来查看字符串“看起来”是否像有效的十进制。 Here is one attempt: 这是一种尝试:

CASE WHEN tew_userdata_locTo.use_data0 not like '%[^0-9.]%' and
          tew_userdata_locTo.use_data0 not like '%.%.%'
     THEN convert(decimal, tew_userdata_locTo.use_data0)
THEN .. ELSE .. END

I wrote a useful scalar function to simulate the TRY_CAST function of SQL SERVER 2012 in SQL Server 2008. 我编写了一个有用的标量函数来模拟SQL Server 2008中SQL SERVER 2012的TRY_CAST函数。

dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)

The two main differences with TRY_CAST Function fo SQL Server 2012 are that you must pass 3 parameters and you must additionally perform an explicit CONVERT or CAST to the field. SQL Server 2012的TRY_CAST函数的两个主要区别在于,您必须传递3个参数,并且还必须对该字段执行显式的CONVERT或CAST。 However, it is still very useful because it allows you to return a default value if CAST is not performed correctly. 但是,它仍然非常有用,因为如果未正确执行CAST,它允许您返回默认值。

FUNCTION CODE: 功能码:

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 
                    --E. B. 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 
------------------------------------------------------------------------------------------------------------------------
*/

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
        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 = 'DATE'
    BEGIN
        IF ISDATE(@pExpression) = 1
        BEGIN
            DECLARE @pExpressionDATE AS DATE = cast(@pExpression AS DATE)

            RETURN cast(@pExpressionDATE as DATE)
        END
        ELSE 
        BEGIN
            RETURN @pReturnValueIfErrorCast
        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

For now only supports the data types INT, DATE, NUMERIC, BIT and FLOAT . 目前仅支持INT,DATE,NUMERIC,BIT和FLOAT数据类型。 You can find the last versión of this code in the next link below and we help each other to improve it. 您可以在下面的下一个链接中找到此代码的最新版本,我们会互相帮助进行改进。 TRY_CAST Function for SQL Server 2008 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb SQL Server 2008的TRY_CAST函数 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb

One other solution makes use of CLR assemblies. 另一种解决方案是使用CLR程序集。 This allows to "link" functions written in C# or VB which will run much faster than any SQL equivalent. 这允许“链接”用C#或VB编写的函数,这些函数的运行速度比任何SQL等效函数都快得多。

The following article describes how to create a CLR assembly within a professional version of Visual Studio. 下面的文章介绍了如何在Visual Studio的专业版本中创建CLR程序集。 However, this can be created by any other version (ie Community) as a class library and manually registered within database assemblies. 但是,可以通过任何其他版本(即社区)将其创建为类库,并在数据库程序集中手动注册。

For current problem, the check be simply done using the following class: 对于当前问题,只需使用以下类即可完成检查:

[Microsoft.SqlServer.Server.SqlFunction]
public static SqlBoolean IsDecimal(string value)
{
    decimal decValue;
    bool isNumeric = decimal.TryParse(value, out decValue);
    return new SqlBoolean(isNumeric);
}

Usage example: 用法示例:

select dbo.IsDecimal(1.0) --> 1
select dbo.IsDecimal(1.0a) --> 0

-- quick and dirty performance test -> takes about 2 seconds (250K records)
-- as opposed to about 1 second of just selecting the data without any filter 
select * from sys.messages where dbo.IsDecimal(severity) = 1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM