避免TSQL数据转换错误

时间:2020-03-06 15:03:34  来源:igfitidea点击:

我认为最好以一个简单的例子的形式提出。下面的SQL块会导致" DB库错误:20049严重性:4消息:数据转换导致溢出"消息,但是怎么回事呢?

declare @a numeric(18,6), @b numeric(18,6), @c numeric(18,6)
select @a = 1.000000, @b = 1.000000, @c = 1.000000
select @a/(@b/@c)
go

这与以下内容有何不同?

select 1.000000/(1.000000/1.000000)
go

哪个工作正常?

解决方案

这仅仅是推测,但是可能是DBMS不在查看变量的动态值,而是仅查看潜在值?因此,将六进制数字除以六进制数字可能会导致十二进制数字。在字面上的划分中,DBMS知道没有溢出。仍然不确定为什么DBMS会关心-它是否不应该返回两个六进制十进制除法的结果,直到一个18进制十进制数呢?

因为我们已经在第一个示例中声明了变量,所以结果应该是相同的声明(即数值(18,6)),但事实并非如此。

我不得不说,第一个在SQL2005中工作(返回1.000000 [相同的声明类型]),而第二个返回了(1.00000000000000000000000 [完全不同的声明])。

我上次尝试使用Sybase时遇到了相同的问题(很多年前)。从SQL Server的心态出发,我没有意识到Sybase会试图强制将小数点强制掉-从数学上讲,这是它应该做的。 :)

从Sybase手册中:

Arithmetic overflow errors occur when
  the new type has too few decimal
  places to accommodate the results.

再往下走:

During implicit conversions to numeric
  or decimal types, loss of scale
  generates a scale error. Use the
  arithabort numeric_truncation option
  to determine how serious such an error
  is considered. The default setting,
  arithabort numeric_truncation on,
  aborts the statement that causes the
  error but continues to process other
  statements in the transaction or
  batch. If you set arithabort
  numeric_truncation off, Adaptive
  Server truncates the query results and
  continues processing.

因此,假设在情况下精度损失是可以接受的,那么我们可能希望在交易开始时进行以下操作:

SET ARITHABORT NUMERIC_TRUNCATION OFF

然后在交易结束时:

SET ARITHABORT NUMERIC_TRUNCATION ON

这就是很多年前为我解决的问题...