【问题标题】:SQL ROUND - What is the actual reason for arithmetic overflow?SQL ROUND - 算术溢出的实际原因是什么?
【发布时间】:2016-03-09 16:18:56
【问题描述】:

我的任务是在工作中调试我们的一些旧代码,但遇到了一个问题。代码是用 VB6 编写的,我们有一个名为 RoundIt() 的函数,它接受一个值,然后将其四舍五入到小数点后 2 位。令人惊讶的是,该函数(我在 VB6 方面没有太多经验,所以我假设它可能只是语言的限制)构建了一个 SQL 字符串来执行以对值进行舍入。

所以在代码中我们有一个双精度类型变量,我将其命名为myVal。在这种特殊情况下,我们得到一个错误,因为myVal 的值是0.997721736173984,并且构建的字符串变成了

 SELECT ROUND(0.997721736173984, 2) as RoundedNum

这会导致消息“将表达式转换为数字数据类型时出现算术溢出错误”。据我了解,这是由于试图将值四舍五入为 1 但无法这样做,因为现在返回的数据类型与 ROUND 函数中输入的数据类型不同,并且它们必须相同。

我的问题是,因为这是一个动态构建的 SQL 字符串,所以不像我们声明一个具有数据类型的 SQL 变量并在 ROUND 函数中使用它,我们只是构建字符串 - 所以 什么默认情况下0.997721736173984的数据类型到底是什么?那么尝试返回的数据类型是什么?我猜是小数(不确定精度或小数位数),当它尝试返回四舍五入时,精度或小数位数现在不同了价值,但我想确定一下。

我不是要求避免算术溢出,或确定不同服务器上的差异,所以这个问题与建议的内容不重复。我的问题是动态构建的 SQL 字符串输入/输出的数据类型是什么,为什么会导致算术溢出错误(如果根据下面的评论,它们属于相同的数据类型)。

【问题讨论】:

  • 你确定是MS SQL ServerROUND 需要 2 个参数。
  • 我的错误,我忘了添加那部分,更新的问题。
  • 什么版本的 SQL 服务器?当我将您的代码扔进 SQL 2012 时,它没有运行。也就是说,我的想法是 SQL Server 期望像 numeric(x,y) 这样的东西,并且总位数大于 x。
  • NUMERIC(15,15) Demo
  • 不要修复查询,修复函数 - 这是浪费且毫无意义的。我 99% 确定 VB6 有 Round() 函数——至少 VBA 有。如果没有,您可以使用基本数学或将值格式化为两位小数,然后将其转换回数字,轻松编写一个。不要在每次需要执行基本数学运算时都进行服务器往返。

标签: sql sql-server double decimal rounding


【解决方案1】:

如果你执行这段代码:

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 如何解析表达式中的数字数据类型的一些更有用的信息:

Precision, Scale, and Length (Transact-SQL)

【讨论】:

  • 谢谢布赖恩,我正在寻找可以执行的东西来确定数据类型,这绝对有帮助。四舍五入的数字不会具有相同的数据类型是有道理的...您是否偶然知道四舍五入的数字的数据类型是什么?
  • 我为您添加了更多信息。
【解决方案2】:

您可以使用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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-13
    • 1970-01-01
    • 1970-01-01
    • 2011-09-15
    • 2010-11-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多