【问题标题】:Decimal with high precision and scale creating 'Arithmetic Overflow Error'具有高精度和比例的十进制创建“算术溢出错误”
【发布时间】:2015-11-22 09:19:06
【问题描述】:

我使用 BCP 将大量数据导入到包含所有字符串的 SQL Server 中。为了进一步保持数据库的完整性,我将更改所有列以获取正确的元数据。

我有一个实际包含十进制值的当前字符串列,需要更改该列以使其成为十进制(p,s)。

由于数据量大,我写了如下查询来获取小数点前后字符串的最大长度;从小数点前后添加字符串的长度以获得精度。以下查询将显示我到达的位置:

SELECT 
    '('
    +
    CAST(MAX(LEN(LEFT(Measure, LEN(Measure) - LEN(SUBSTRING(Measure, CHARINDEX('.', Measure) , 255))))) 
    + 
    MAX(LEN(SUBSTRING(Measure, CHARINDEX('.', Measure) +1, 255))) AS varchar(5)) 
    + 
    ',' 
    + CAST(MAX(LEN(SUBSTRING(Measure, CHARINDEX('.', Measure) +1, 255))) AS varchar(5))
    +
    ')' AS 'max[p,s]'
FROM
    Result

结果如下:

max[p,s]
--------
(24,19)

当我通过陈述以下内容更改列时:

ALTER TABLE Result
ALTER COLUMN Measure DECIMAL (24,19)

我收到以下错误:

消息 8115,级别 16,状态 8,第 1 行算术溢出错误 将 varchar 转换为数字数据类型。该声明已 终止。

(p,s) 其中 p = 精度和 s = 比例;我的印象是我的最大精度 (p) 可以达到 38。为什么会出现此错误?有什么建议?我必须不丢失任何精度。

【问题讨论】:

  • 您确定该列中没有任何非数字值吗?
  • @APH 列中没有非数字值

标签: sql-server sql-server-2012 decimal precision


【解决方案1】:

您可以尝试使用以下方法查找错误行:

select Measure from Result 
where TRY_CONVERT(DECIMAL(24,19),Measure)  
is Null And Measure is Not Null;

【讨论】:

  • 这会导致误报,因为精度不正确。
  • 我已经使用@Jeff Orris 查询运行了一些测试,以找到所需的精度,结果证明是正确的。我相信某处有一些非数字值。
  • @JonC 我运行了建议的查询并得到以下 2 个结果:132000116000....为什么这些不能转换为十进制?
  • 显然是因为它试图将 19 位小数精度添加到这些整数上,然后你最终得到更高的精度(小数点左右超过 23 位)。如果您将它们更改为 132000.0 和 116000.0 但由于它们没有浮点数,那么它会尝试使用提供的精度从整数转换为小数。
  • @JonC 感谢您建议运行您的查询。问题是我发布的查询不适用于表示为字符串的整数。所以首先我必须得到字符串整数的最大长度,然后查询值 LIKE '%.%'。获得小数点右边的最大 len。我会接受你的回答,因为这让我知道我需要做什么(更改为 varchar(25,19))。我将添加另一个答案,以更改我在问题中发布的查询以获得所需的真实最大值(p,s),以防有没有“。”的值。
【解决方案2】:

来自 MSDN decimal and numeric (Transact-SQL)

p(精度) 将存储的小数位数的最大总数,小数点的左侧和右侧。精度必须是从 1 到最大精度 38 之间的值。默认精度是 18。

要获得精度使用:

SELECT  MAX(LEN(Measure)) AS max_precision_needed 
FROM    Result

上面的查询没有减去小数点,因为字符串可能包含一个没有小数点的整数。它为您提供所需的最大可能值。

示例: 数字 123.00 需要具有以下精度的字段;十进制(5,2)

在以下示例中,计算所需精度后,可以进行转换。

   DECLARE @measures TABLE ( measure VARCHAR(50) );

   INSERT   INTO @measures( measure ) VALUES   ( '123' )
   INSERT   INTO @measures( measure ) VALUES   ( '123.00' )
   INSERT   INTO @measures( measure ) VALUES   ( '123.001' )
   INSERT   INTO @measures( measure ) VALUES   ( '.123' )
   INSERT   INTO @measures( measure ) VALUES   ( '0.123' )
   INSERT   INTO @measures( measure ) VALUES   ( '   ' )

   SELECT   '(' + CAST(MAX(LEN(measure)) - 1 AS VARCHAR(5)) + '.' + CAST(MAX(LEN(SUBSTRING(measure, CHARINDEX('.', measure) + 1, 255))) AS VARCHAR(5)) + ')'
   FROM     @measures

   SELECT   ISNULL(CONVERT(DECIMAL(6, 3), CASE WHEN CHARINDEX('.', measure) = 0 THEN measure + '.0' ELSE measure END), 0.0)
   FROM     @measures

在此示例中,转换不应因字符串中的整数或空字符串而失败。

【讨论】:

  • 如果您尝试使用您的示例 (123.00) 进行@Jeff Orris 查询,您会得到 (5,2) 作为所需的精度,他的查询实际上是计算存储在小数点左右的数字正如你所说的那样。
  • 建议查询将计算小数点,因此 p 将比需要的多 1
  • 这是正确的@JeffOrris,我添加了一条评论来表明这一事实。谢谢。
猜你喜欢
  • 2013-04-09
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
  • 1970-01-01
  • 2015-10-29
  • 2010-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多