我的任务是在工作中调试一些旧代码,并遇到了一个问题。该代码是用 VB6 编写的,我们有一个名为 RoundIt()
的函数,它接受一个值,然后将其四舍五入到小数点后两位。令人惊讶的是,该函数(我在 VB6 方面没有太多经验,所以我假设它可能只是该语言的限制)构建一个要执行的 SQL 字符串,以便对值进行四舍五入。
因此,在代码中我们有一个 double 型变量,我将其称为 myVal
。在这种特殊情况下,我们收到错误,因为 myVal
的值为 0.997721736173984
,并且构建的字符串变为
SELECT ROUND(0.997721736173984, 2) as RoundedNum
这会导致消息“将表达式转换为数据类型数字时出现算术溢出错误”。据我了解,这是由于值试图舍入为 1 但无法这样做,因为现在返回的数据类型与 ROUND 函数中输入的数据类型不同,而这些数据类型必须相同。
我的问题是,由于这是一个动态构建的 SQL 字符串,因此我们不像使用数据类型声明 SQL 变量并在 ROUND 函数中使用它,我们只是构建字符串 - 那么什么默认情况下的数据类型到底是0.997721736173984
?那么尝试返回的数据类型是什么?我猜测是小数(不确定精度或小数位数),并且当它尝试返回四舍五入时,精度或小数位数现在不同值,但我只是想确定一下。
我并不是要求避免算术溢出,或确定不同服务器上的差异,因此这个问题与建议的内容不重复。我的问题是动态构建的 SQL 字符串的输入/输出是什么数据类型,以及为什么这会导致算术溢出错误(如果根据下面的注释,它们具有相同的数据类型)。
最佳答案
如果执行此代码:
select 0.097721736173984 RoundedNum
into #temp
exec tempdb.dbo.sp_help '#temp'
您会发现它将您的文字数字解释为数字(15,15)。
Column_name Type Computed Length Prec Scale Nullable TrimTrailingBlanks FixedLenNullInSource Collation
----------- ------- --------- ------ ----- ----- --------- ------------------- --------------------- ---------
RoundedNum numeric no 9 15 15 no (n/a) (n/a) NULL
这意味着您只能使用 15 位数字,并且所有 15 位都必须位于小数点右侧。当您舍入此数字时,它不再适合数据类型,因此会返回错误。
您可以通过显式转换文字来修复它。如:
select round(convert(float,0.997721736173984),2) RoundedNum
您可以选择您想要的任何数据类型,只要它捕获所有预期的有效数字即可。 Float
为您提供了最大的范围灵活性,但需要权衡潜在的准确性损失。如果您知道要四舍五入的所有数字都将小于 1 并且不超过 15 位数字,则 numeric(16,15)
将保存您的原始四舍五入数字。使用数字类型时,您只需考虑数字所属的范围,并确保已分配足够的空间来容纳所有可能的结果。
以下是有关 SQL Server 如何解析表达式中的数字数据类型的一些更有用的信息:
关于SQL ROUND - 算术溢出的实际原因是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35896964/