SQL Server : error converting data type varchar to numeric

后端 未结 5 1944
谎友^
谎友^ 2020-12-25 12:36

I have a table:

Account_Code | Desc
503100       | account xxx
503103       | account xxx
503104       | account xxx
503102A      | account xxx
503110B               


        
5条回答
  •  不思量自难忘°
    2020-12-25 13:11

    SQL Server 2012 and Later

    Just use Try_Convert instead:

    TRY_CONVERT takes the value passed to it and tries to convert it to the specified data_type. If the cast succeeds, TRY_CONVERT returns the value as the specified data_type; if an error occurs, null is returned. However if you request a conversion that is explicitly not permitted, then TRY_CONVERT fails with an error.

    Read more about Try_Convert.

    SQL Server 2008 and Earlier

    The traditional way of handling this is by guarding every expression with a case statement so that no matter when it is evaluated, it will not create an error, even if it logically seems that the CASE statement should not be needed. Something like this:

    SELECT
       Account_Code =
          Convert(
             bigint, -- only gives up to 18 digits, so use decimal(20, 0) if you must
             CASE
             WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL
             ELSE X.Account_Code
             END
          ),
       A.Descr
    FROM dbo.Account A
    WHERE
       Convert(
          bigint,
          CASE
          WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL
          ELSE X.Account_Code
          END
       ) BETWEEN 503100 AND 503205
    

    However, I like using strategies such as this with SQL Server 2005 and up:

    SELECT
       Account_Code = Convert(bigint, X.Account_Code),
       A.Descr
    FROM
       dbo.Account A
       OUTER APPLY (
          SELECT A.Account_Code WHERE A.Account_Code NOT LIKE '%[^0-9]%'
       ) X
    WHERE
       Convert(bigint, X.Account_Code) BETWEEN 503100 AND 503205
    

    What this does is strategically switch the Account_Code values to NULL inside of the X table when they are not numeric. I initially used CROSS APPLY but as Mikael Eriksson so aptly pointed out, this resulted in the same error because the query parser ran into the exact same problem of optimizing away my attempt to force the expression order (predicate pushdown defeated it). By switching to OUTER APPLY it changed the actual meaning of the operation so that X.Account_Code could contain NULL values within the outer query, thus requiring proper evaluation order.

    You may be interested to read Erland Sommarskog's Microsoft Connect request about this evaluation order issue. He in fact calls it a bug.

    There are additional issues here but I can't address them now.

    P.S. I had a brainstorm today. An alternate to the "traditional way" that I suggested is a SELECT expression with an outer reference, which also works in SQL Server 2000. (I've noticed that since learning CROSS/OUTER APPLY I've improved my query capability with older SQL Server versions, too--as I am getting more versatile with the "outer reference" capabilities of SELECT, ON, and WHERE clauses!)

    SELECT
       Account_Code =
          Convert(
             bigint,
             (SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%')
          ),
       A.Descr
    FROM dbo.Account A
    WHERE
       Convert(
          bigint,
          (SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%')
       ) BETWEEN 503100 AND 503205
    

    It's a lot shorter than the CASE statement.

提交回复
热议问题