我的任务是在工作中调试一些旧代码并遇到问题。代码是用VB6编写的,我们有一个名为RoundIt()
的函数,它接受一个值,然后将其舍入到2个小数位。令人惊讶的是,这个函数(我在VB6中没有太多经验,所以我假设它可能只是语言的限制)构建一个SQL字符串来执行以循环该值。
所以在代码中我们有一个双类型变量,我称之为myVal
。在这种特殊情况下,我们收到错误,因为myVal
的值为0.997721736173984
,并且构建的字符串变为
SELECT ROUND(0.997721736173984, 2) as RoundedNum
导致消息“将表达式转换为数据类型数字的算术溢出错误”。根据我的理解,这是由于值试图舍入为1但无法这样做,因为现在返回的数据类型与ROUND函数中输入的数据类型不同,并且那些必须相同。
我的问题是,由于这是一个动态构建的SQL字符串,它不像我们用数据类型声明一个SQL变量并在ROUND函数中使用它,我们只是构建字符串 - 所以是什么确切地说,0.997721736173984
的数据类型是默认的吗?那么什么是试图返回的数据类型呢?我猜一个小数(不确定精度或比例),并且当它试图返回舍入时精度或比例现在是不同的价值,但我只想确定。
我不是要求避免算术溢出,或者确定不同服务器上的差异,因此这个问题与建议的内容不重复。我的问题是从动态构建的SQL字符串输入/输出什么数据类型,以及为什么会导致算术溢出错误(如果根据下面的注释,它们具有相同的数据类型)。
答案 0 :(得分:4)
如果您执行此代码:
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如何解析表达式中数值数据类型的更多有用信息:
答案 1 :(得分:0)
您可以使用SQL_VARIANT_PROPERTY功能查找数据类型。
SELECT
SQL_VARIANT_PROPERTY(x.y, 'BaseType') AS DataType,
SQL_VARIANT_PROPERTY(x.y, 'Precision') AS Precision,
SQL_VARIANT_PROPERTY(x.y, 'Scale') AS Scale
FROM
(
VALUES
(0.997721736173984)
) AS x(y)
;
在这种情况下,你有一个数字(15,15)。由于所有可用空间都分配给了比例,因此无法舍入为1。
使用CAST明确设置数据类型将解决您的问题。
SELECT
ROUND(CAST(0.997721736173984 AS NUMERIC(16, 15)), 2)