2014-03-26 206 views
0

我有一個針對SQL Server 2012編寫的查詢,並使用了try_convert()函數。我現在需要在SQL Server 2008 R2 Express上執行查詢,並且我的理解是try_convert()不是2008 R2中支持的功能。SQL Server 2008 R2 Express中的try_convert

我目前的查詢中包含此塊:

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

這給了我一個真或假的結果讓我知道,如果我能在特定的值轉換爲十進制。如果我可以轉換價值,我這樣做。如果不是,我使用默認的十進制值並繼續。

關於我能寫什麼的任何想法都可以在SQL Server 2008 R2中使用?

+0

谷歌的 「SQL Server 2008 R2嘗試轉換」 –

回答

1

try_convert()嘗試進行轉換。這是處理轉換錯誤的一種很好的方式(或者至少比SQL Server以前做的更好,這是沒有的)。您可以使用like來查看字符串「看起來」是否像有效的小數。這是一個嘗試:

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 
+0

愛它!謝謝。 –

0

我寫了一個有用的標量函數來模擬SQL SERVER 2012的TRY_CAST功能在SQL Server 2008中

dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast) 

與TRY_CAST功能FO SQL的兩個主要區別Server 2012是您必須傳遞3個參數,並且您必須另外執行顯式CONVERT或CAST到該字段。 但是,它仍然非常有用,因爲如果CAST未正確執行,它允許您返回默認值 。

功能碼:

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 

現在只支持數據類型INT,DATE,NUMERIC,BIT和FLOAT。你可以在下面的鏈接中找到這段代碼的最後一個版本,我們互相幫助改進它。 SQL Server 2008的TRY_CAST函數https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb

+0

@ martijn-pieters,Martijn Pieters謝謝你教我什麼不該做 – JotaPardo

0

另一個解決方案使用CLR程序集。這允許「鏈接」用C#或VB編寫的函數,其運行速度比任何SQL等效的要快得多。

以下article描述瞭如何在專業版本的Visual Studio中創建CLR程序集。但是,這可以通過任何其他版本(即社區)作爲類庫創建並在數據庫程序集中手動註冊。

對於目前存在的問題,檢查使用下面的類做簡單:

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

用例:

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 
相關問題