【问题标题】:SQL IsNumeric not workingSQL IsNumeric 不起作用
【发布时间】:2011-09-27 10:41:24
【问题描述】:

保留列是一个 varchar,要对其进行求和,我想将其转换为十进制。 但是下面的 SQL 给了我一个错误

select
cast(Reserve as decimal)
from MyReserves

将数据类型 varchar 转换为数值时出错。

我添加了 isumeric 而不是 null 以尝试避免此错误,但它仍然存在,有什么想法吗?

select
cast(Reserve as decimal)
from MyReserves
where isnumeric(Reserve ) = 1
and MyReserves is not null

【问题讨论】:

  • 你能在 Reserve 列显示一些数据吗?
  • 您必须小心使用 isnumeric,因为它可能返回不正确的结果。这来自文档 **ISNUMERIC 对于某些不是数字的字符(例如加号 (+)、减号 (-))和有效的货币符号(例如美元符号 ($))返回 1。 ** 看看stackoverflow.com/questions/312054/…
  • @astander - 它不会返回不正确的结果。只是大多数人不理解它回答了一个没人真正想问的问题(“这个字符串会转换为至少一种数字类型吗?”)
  • 您似乎假设cast 将在where 之后发生。无论isnumeric 的限制如何,在任何情况下都绝对不能保证这一点
  • @MartinSmith - 我认为你应该添加这个作为答案

标签: sql sql-server


【解决方案1】:

请看这里:CAST and IsNumeric

试试这个:

WHERE IsNumeric(Reserve + '.0e0') = 1 AND reserve IS NOT NULL

更新

十进制的默认值是(18,0),所以

declare @i nvarchar(100)='12121212121211212122121'--length is>18 
SELECT ISNUMERIC(@i) --gives 1
SELECT CAST(@i as decimal)--throws an error

【讨论】:

    【解决方案2】:

    天哪,似乎没有人正确解释这一点。 SQL 是一种描述性语言。它没有指定操作的顺序。

    您(嗯,曾经)遇到的问题是where 在转换发生之前没有进行过滤。但是,对于 case 语句,操作顺序是有保证的。因此,以下将起作用:

    select cast(case when isnumeric(Reserve) = 1 then Reserve end as decimal)
    from MyReserves
    where isnumeric(Reserve ) = 1 and MyReserves is not null
    

    此问题与您要转换为的特定数字格式或isnumeric() 函数无关。只是不能保证操作的顺序。

    【讨论】:

      【解决方案3】:

      看来isnumeric有一些问题:

      http://www.sqlhacks.com/Retrieve/Isnumeric-problems (通过互联网档案)

      根据该链接,您可以这样解决:

      select
      cast(Reserve as decimal)
      from MyReserves
      where MyReserves is not null
      and MyReserves * 1 = MyReserves 
      

      【讨论】:

      • 让我们试试:声明@i nvarchar(100)='+' IF ISNUMERIC(@i)=1 AND @i*1=@i PRINT 'Numeric' ELSE PRINT 'NON-Numeric' - --- 结果:数字(不正确)这是因为 SELECT ('+' * 1) 给你 '0'
      【解决方案4】:

      使用 try_cast (sql 2012)

      select
      try_cast(Reserve as decimal)
      from MyReserves
      

      【讨论】:

        【解决方案5】:

        IsNumeric 是一个有问题的孩子 -- SQL 2012 及更高版本有TRY_CAST and TRY_CONVERT

        如果您使用的是早期版本,那么您可以编写一个将转换为小数的函数(如果不转换,则为 NULL)。这使用了当数字不合适时不会抛出错误的 XML 转换函数;)

        -- Create function to convert a varchar to a decimal (returns null if it fails)
        IF EXISTS( SELECT * FROM sys.objects WHERE object_id = OBJECT_ID( N'[dbo].[ToDecimal]' ) AND type IN( N'FN',N'IF',N'TF',N'FS',N'FT' ))
            DROP FUNCTION [dbo].[ToDecimal];
        GO
        
        CREATE FUNCTION ToDecimal
        (   
            @Value VARCHAR(MAX)
        )
        RETURNS DECIMAL(18,8)
        AS
        BEGIN
            -- Uses XML/XPath to convert @Value to Decimal because it returns NULL it doesn't cast correctly
            DECLARE @ValueAsXml XML
            SELECT @ValueAsXml = Col FROM (SELECT (SELECT @Value as Value FOR XMl RAW, ELEMENTS) AS Col) AS test
            DECLARE @Result DECIMAL(38,10)
            -- XML/XPath will return NULL if the VARCHAR can't be converted to a DECIMAL(38,10)
            SET @Result =  @ValueAsXml.value('(/row/Value)[1] cast as xs:decimal?', 'DECIMAL(38,10)')
            RETURN CASE -- Check if the number is within the range for a DECIMAL(18,8)
                WHEN @Result >= -999999999999999999.99999999 AND @Result <= 999999999999999999.99999999
                    THEN CONVERT(DECIMAL(18,8),@Result) 
                ELSE 
                    NULL
            END
        END
        

        然后只需将您的查询更改为:

        select dbo.ToDecimal(Reserve) from MyReserves
        

        【讨论】:

          【解决方案6】:

          isnumeric 在 SQL 中并非 100% 可靠 - 请参阅此问题 Why does ISNUMERIC('.') return 1?

          我猜你在通过isnumeric 测试但不会转换为十进制的保留列中有值。

          【讨论】:

            【解决方案7】:

            只是对数字的提醒;如果字符串包含一些数字和“E”后跟一些数字,则将其视为指数。例如,select isumeric('123E0') 返回 1。

            【讨论】:

            • 对 'D' 也是一样的尝试:SELECT IsNumeric('123D45') As [Useless Function]
            • 哇。我不敢相信。文档根本没有提到这一点docs.microsoft.com/en-us/sql/t-sql/functions/…这也是我遇到的问题。我想我会用 try_cast 替换它
            【解决方案8】:

            我遇到了同样的问题,结果证明是科学记数法,例如 '1.72918E-13' 要找到它,只需执行 where Reserve LIKE '%E%'。尝试绕过这些,看看它是否有效。您必须编写代码将这些转换为可用的东西或重新格式化您的源文件,这样它就不会使用科学记数法存储任何数字。

            【讨论】:

              【解决方案9】:

              IsNumeric 在您的场景中可能并不理想,因为在此 MSDN page 上突出显示的注释显示“ISNUMERIC 为某些不是数字的字符返回 1,例如加号 (+)、减号 (-) 和有效的货币符号,例如美元符号 ($)。”

              还有一篇很好的文章here 进一步讨论了ISNUMERIC

              【讨论】:

                【解决方案10】:

                尝试(例如):

                select
                cast(Reserve as decimal(10,2))
                from MyReserves
                

                数字/小数通常需要精度和比例。

                【讨论】:

                  【解决方案11】:

                  我也面临这个问题,我通过以下方法解决了。我分享这个是因为它可能对某些人有帮助。

                  declare @g varchar (50)
                   set @g=char(10)
                   select isnumeric(@g),@g, isnumeric(replace(replace(@g,char(13),char(10)),char(10),''))
                  

                  【讨论】:

                    猜你喜欢
                    • 2011-07-22
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-01-20
                    • 2012-02-12
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-04-15
                    相关资源
                    最近更新 更多