将varchar转换为float的错误

时间:2011-11-10 19:13:49

标签: sql-server tsql

将varchar转换为float时出错。

拥有一个包含varchar数据的列result的表(不是我的)。我想转换为浮点数以获取所有值> 180.0。

SELECT result FROM table WHERE result > 180.0

产生错误。有趣的是,但是:

WITH temp AS (
  CASE
    WHEN ISNUMERIC(result)=1 THEN CAST(result as FLOAT)
    ELSE CAST(-1.0 AS FLOAT)
  END AS result
)

这很好。当我与它配对时:

SELECT temp.result FROM temp WHERE temp.result > 180.0

我再次收到错误。思考?

更新:完整代码请求...

WITH temp AS (
  SELECT
  CASE
    WHEN ISNUMERIC(result)=1 THEN CAST(result as FLOAT)
    ELSE CAST(-1.0 AS FLOAT)
  END AS result
  FROM table)

SELECT temp.result FROM temp WHERE temp.result > 180.0

使用SQL-SERVER 2008 RC2。

3 个答案:

答案 0 :(得分:9)

这意味着表中至少有一行无法转换为float。执行CASE是安全的,但是在编写T-SQL时,将CTE和添加WHERE子句组合成程序员的常见谬误:声明顺序意味着执行顺序。程序员习惯于C语言的命令式程序风格,并且无法理解SQL的基于声明集的本质。我之前写过关于这个问题的文章,并在谬误导致错误时举例说明:

一旦发布完整的代码,我们就可以看到你在哪种情况下确实在你的情况下犯了谬误并且假定了一定的执行顺序。

更新后

好的,所以我必须管理你的情况下代码在执行顺序是正确的,result列不能在没有首先评估CASE的情况下进行投影。如果CASE在WHERE子句中,则会有所不同。

您的问题不同:ISNUMERIC。这个函数非常慷慨地理解NUMERIC的含义并且之前已经咬过许多开发人员。即,它接受CAST和CONVERT拒绝的值。就像包含逗号的那些:

declare @n varchar(8000) = '1,000';
select isnumeric(@n);
select cast(@n as float);
select case when isnumeric(@n)=1 then cast(@n as float) else null end;

因此,您拥有通过ISNUMERIC测试但未能转换的值。只是抬起头来,你越是深入研究这种方法,你会发现越多的门。只是没有安全的方法来进行服务器端所需的转换。理想情况下,修复数据模型(如果存储浮点数,则使该字段为浮点数)。除此之外,对数据进行分类并删除所有不正确的浮点值,并修复前端/应用程序不再引入新的值,然后添加一个约束,如果出现新的错误值,将触发错误。你将无法在一个查询中解决这个问题,那条路上到处都是尸体。

使用下一版本的SQL Server,您将拥有一个新功能TRY_CONVERT,可以解决您的问题。

答案 1 :(得分:3)

检查出来:

http://classicasp.aspfaq.com/general/what-is-wrong-with-isnumeric.html

基本上,ISNUMERIC函数存在一些已知问题/奇怪之处。该文章的作者建议创建一个新函数并使用它而不是ISNUMERIC。我使用您建议的1,0值对其进行了测试,但它似乎有效。


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 

答案 2 :(得分:2)

只是一个明确的转换,但有一点巧思的REPLACE

SELECT result FROM table WHERE CONVERT(float,REPLACE(result,',','.')) > 180.0