SQL Server 2008 R2 数据类型转换错误

Posted

技术标签:

【中文标题】SQL Server 2008 R2 数据类型转换错误【英文标题】:SQL Server 2008 R2 data type conversion error 【发布时间】:2015-01-31 22:34:00 【问题描述】:

我有一列数据类型为varchar,其中一些包含数字(十进制)值。我需要找到数值> 100的所有行。我使用ISNUMERIC过滤包含数字的行,之后我尝试使用CASTCONVERT将数字更改为decimal价值。我读了另一个建议,说使用子查询可以解决问题,但我也试过了,但没有运气

这是我收到的错误消息:

将 varchar 转换为数字数据类型时出现算术溢出错误。

代码:

select field_name
from table_name
where ISNUMERIC(field_name) = 1  and
      field_name in (select cast(field_name as decimal)
                     from table_name
                     where ISNUMERIC(field_name) = 1
                       and field_name not like '%,%'
                       and field_name not like '%-%'
                       and field_name != '.'
                       and field_name > 100.00)

【问题讨论】:

【参考方案1】:

这是您的查询:

select field_name
from table_name
where ISNUMERIC(field_name) = 1  and
      field_name in (select cast(field_name as decimal)
                     from table_name
                     where ISNUMERIC(field_name) = 1 and
                          field_name not like '%,%' and
                          field_name not like '%-%' and
                          field_name <> '.' and
                          field_name > 100
                    )

您假设where 子句在select 之前被过滤。这不是真的。事实上,SQL Server 将cast() 推送到读取数据的操作。然后它会应用过滤器。

事实上,子查询和公用表表达式也不能保证求值的顺序。什么是case(至少在这种情况下不涉及聚合)。因此,您可以将查询编写为:

select field_name
from table_name
where (case when ISNUMERIC(field_name) = 1 and
                 field_name not like '%,%' and
                 field_name not like '%-%' and
                 field_name <> '.'
            then cast(field_name as decimal)
       end) > 100;

我认为没有理由使用子查询。

【讨论】:

【参考方案2】:

查询存在一些问题,但导致错误的主要问题相当简单:您的field_name &gt; 100.00 条件在执行以下操作之前未将VARCHAR 字段转换为数字类型比较。将其更改为 CONVERT(DECIMAL(38, 18), [field_name]) 将修复您看到的错误。

运行以下一次以查看它是否有效,然后取消注释 AND [field_name] &gt; 100.00 行并再次运行,您将收到您所看到的关于“将 varchar 转换为数据类型数字的算术溢出错误”的错误:

DECLARE @table_name TABLE (field_name VARCHAR(50) NOT NULL);
INSERT INTO @table_name (field_name) VALUES ('1234.5678');
INSERT INTO @table_name (field_name) VALUES ('12.78');
INSERT INTO @table_name (field_name) VALUES ('s');
INSERT INTO @table_name (field_name) VALUES ('2015-01-30');
INSERT INTO @table_name (field_name) VALUES ('51234.5678');
INSERT INTO @table_name (field_name) VALUES ('987651234.56789124');

SELECT field_name
FROM   @table_name
WHERE  ISNUMERIC([field_name]) = 1
AND    [field_name] IN (
                      SELECT CAST([field_name] AS DECIMAL)
                      FROM   @table_name
                      WHERE  ISNUMERIC([field_name]) = 1
                      AND    [field_name] NOT LIKE '%,%'
                      AND    [field_name] NOT LIKE '%-%'
                      AND    [field_name] != '.'
--                      AND    [field_name] > 100.00
                      AND    CONVERT(DECIMAL(38, 18), [field_name]) > 100.00
                     );

但是,还是有一些问题:

    如问题所示,子查询返回转换值列表毫无意义。查询可能已被更改以在此处发布,因此其真实形式更有意义,但不需要子查询,无论如何也无济于事。 CASTDECIMAL 未指定精度和比例。默认值分别为 18 和 0。意思是,您将字符串转换为DECIMAL(18, 0)。这似乎在这里没有任何影响,但最好指定值。运行 SELECT CAST('123.456' AS DECIMAL) 将只返回 123。 您实际上需要过滤掉带有逗号的值,因为这些值可以被删除,从而为您留下一个可以转换的值。当然,除非你有一些像12,34,56,78 这样的代码。但如果它们是有效数字,例如1,234.56,那么您可以使用REPLACE([field_name], ',', '')

    将以下两行添加到上面示例顶部的 INSERTs 错误:

    INSERT INTO @table_name (field_name) VALUES ('1,234.5678');
    INSERT INTO @table_name (field_name) VALUES ('.');
    

所有这些都可以通过执行以下操作来处理(将上面的查询替换为下面的查询,但保留DECLAREINSERTs):

SELECT field_name, CAST([field_name] AS DECIMAL(38, 18)) AS [DecimalValue]
FROM   @table_name
WHERE  ISNUMERIC(field_name) = 1
AND    field_name NOT LIKE '%,%'
AND    field_name NOT LIKE '%-%'
AND    field_name <> '.'
AND    CONVERT(DECIMAL(38, 18), REPLACE([field_name], ',', '')) > 100.00;

【讨论】:

以上是关于SQL Server 2008 R2 数据类型转换错误的主要内容,如果未能解决你的问题,请参考以下文章

sql server 2008 R2连接报错

在 SQL Server 2008 R2 中将 varchar 转换为时间戳

数据库SQL SERVER 2008 R2 时间戳如何转换成时间格式

SQL Server 2008 R2,将列转换为行,将行转换为列[重复]

SQL Server 2008 R2导出数据脚本的方法

SQL SERVER 2008 R2 数据库附加到 SQL server 2005,怎么整