转换 SQL Server 2008 R2 和 2012 之间的差异

Posted

技术标签:

【中文标题】转换 SQL Server 2008 R2 和 2012 之间的差异【英文标题】:Convert differences between SQL Server 2008 R2 and 2012 【发布时间】:2015-08-04 13:08:23 【问题描述】:

我在 SQL Server 2012 中执行 SQL 语句时遇到问题,而它在 SQL Server 2008 R2 中完美运行...

错误信息是:

消息 8114,第 16 级,状态 5,第 1 行 转换数据类型 varchar 时出错 转为数字。

我想执行这条 SQL Select 语句:

Select 
    count(*)
from IMPORTBM 
    inner join ATTRIBUTE on ATT_ATTRIBUTE_ID = IMP_ATTRIBUTE_ID 
where IMP_LOCATION_ID = 2 
    AND IMP_SERIAL_ID = 310001 
    AND IMP_VERSION_ID = 1 
    AND (
        (ATT_ATTRIBUTE = 'PS_APISizing' 
            AND IMP_VALUE = 'C')
        OR
        (ATT_ATTRIBUTE = 'DTD' 
            AND ISNUMERIC(IMP_VALUE) = 1  
            AND  CAST( IMP_VALUE  as NUMERIC(38,19)) <= 0.469) 
        OR
        (ATT_ATTRIBUTE = 'IOD' 
        AND ISNUMERIC(IMP_VALUE) = 1  
        AND  CAST( IMP_VALUE  as NUMERIC(38,19)) BETWEEN 3.684 AND 4.225) 
    )  

您能帮我找出为什么它不适用于 SQL Server 2012 吗?

【问题讨论】:

请注意,如果我删除具有ISNUMERIC 函数的两个where 子句之一,则该sql 语句有效。 这是使用实体-属性-值方案的危险之一... @ErikE 我别无选择,只能使用这个解决方案...... :( 我们在这里讨论油泵的配置。 【参考方案1】:

问题是条件并不总是按照您放置它们的顺序进行评估。这意味着 Cast(IMP_VALUE as numeric(38, 19)) 在它是一个非数字值时可能会失败,如果查询执行计划恰好在 IsNumeric(IMP_VALUE) = 1 之前计算它。

这不是由有多少子句或您正在运行的 SQL Server 版本决定的——这些只是巧合。问题和我描述的完全一样。使用不同的查询得到不同的结果是由于不同的查询或不同的服务器使用不同的执行计划。

解决方法是确保所有条件都不会引发错误,无论它们以什么顺序执行。

对于所有版本的 SQL Server,您都可以这样做:

Convert(
   numeric(38,19),
   CASE WHEN IsNumeric(IMP_VALUE) = 1 THEN IMP_VALUE ELSE NULL END
) <= 0.469

或者,在 SQL Server 2012 及更高版本中,您可以使用Try_Convert:

Try_Convert(numeric(38,19), IMP_VALUE) <= 0.469

如果你的服务器抱怨这不是一个内置函数名,那么很可能你的数据库是从以前的版本升级的,你需要alter the database compatibility level。请注意,这样做可能会在系统中产生一些您需要考虑的其他影响,因此请先对其进行研究,然后在非生产系统中进行尝试,然后再在生产中进行(或在非关键时期进行)如果遇到任何问题,请重新设置兼容性级别)。危险不在于会发生任何损坏,而是查询结果可能不同或可能发生错误(查询或存储过程结果的不同顺序更常见)。

ALTER DATABASE database_name 
SET COMPATIBILITY_LEVEL = 110; -- SQL Server 2012

【讨论】:

感谢您的回答。我尝试了您给我的CASE ... END 解决方案,但出现错误:Arithmetic overflow error converting varchar to data type numeric.。对于兼容性级别,我的数据库是从带有事务复制的 SQL Server 2008 R2 数据库复制的。所以我认为我不能轻易升级兼容级别:S ... 抱歉,我遗漏了ConvertCast。请使用更新后的答案重试。 或者您可以像我一样将整个表达式包装在 Cast 或 Convert 中。 是的,我也试过了,我认为这样更清楚:) 谢谢@ErikE!【参考方案2】:

表的定义是什么? (至少 IMP_VALUE)。为什么不将数字存储在第一位... 2008 和 2012 的查询计划可能不同,并且不会按照相同的顺序处理参数和条件。

使用 SQL Server 2012(兼容级别 110),您可以尝试替换 2 演员:

 AND ISNUMERIC(IMP_VALUE) = 1  
        AND  CAST( IMP_VALUE  as NUMERIC(38,19)) <= 0.469) 

AND TRY_CONVERT(NUMERIC(38,19), IMP_VALUE) <= 0.469
AND  TRY_CONVERT(NUMERIC(38,19), IMP_VALUE) BETWEEN 3.684 AND 4.225

无法转换时返回NULL而不会出错。

【讨论】:

感谢您的回答@Julien Vavasseur。 IMP_Value 是一个 varchar(100) 字段。 IMP_Value 可以是数字或 varchar 值。我尝试了 TRY_CONVERT 但出现错误:“Msg 195, Level 15, State 10, Line 13 TRY_CONVERT' is not a recognized built-in function name. 不要忘记,如果只指定一个带有ISNUMERIC 函数的where 子句,它就会起作用。 您只能将 try convert 与 SQL Server 2012 及其兼容级别 (110) 一起使用。使用 2008 用例时 isumeric() 然后 cast(imp_value as umeric) else null end and isnumeric() and other condition 也不保证评估顺序。对于兼容这两个版本的代码,更安全地说CASE WHEN ISNUMERIC() = 1 THEN CAST(...) END &lt;= 0.469

以上是关于转换 SQL Server 2008 R2 和 2012 之间的差异的主要内容,如果未能解决你的问题,请参考以下文章

Sql Server 2008 R2 - SSMA 转换的扩展过程调用(IMPL 过程)影响性能

如何在 Sql Server 2008 R2 中将列转换为行?

SQL Server 2008 R2 - 将列转换为行并将所有值放在一列中

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

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

在 SQL Server 2008 R2 中对多行进行分组