【问题标题】:SQL Decimal type - Precision and Scale discrepancySQL 十进制类型 - 精度和比例差异
【发布时间】:2013-03-02 19:11:41
【问题描述】:

为了回答这里的另一个问题,我创建了以下数据结构和行:

create table [resource] (Name varchar(16),date datetime, project varchar(16),hours int)
INSERT INTO resource
values ('Andy Sandy', '2013-03-02', 'Enhancements',40)
INSERT INTO resource
values('Fred Jones', '2013-10-02', 'Enhancements',40)

我已经执行了以下查询:

select 
case when sum(hours) > 0 Then
    CAST(SUM(hours) as DECIMAL(5,2))/40
else 0 end as [hours],
[DATE]
from resource group by date

结果如下所示:

Hours           Date
1.000000    2013-03-02 00:00:00.000
1.750000    2013-10-02 00:00:00.000

当我将小时数转换为小数时,我指定了 5 的精度和 2 的刻度。我不明白为什么这个数字是这样的。如果我没有指定精度和比例,那么结果是一样的。这是为什么呢?

【问题讨论】:

  • 我用你提供的数据得到这个:小时 DATE -------------------------------- ------- ----------- 1.000000 2013-03-02 00:00:00.000 1.000000 2013-10-02 00:00 :00.000
  • 除法的结果是numeric(9,6)。将 40 视为numeric(2,0),然后将精度和比例插入the formulas in BOL
  • $&%^% 愚蠢的规则意味着我不能通过发布不适当的答案来合理地格式化这个而不失去代表。
  • 以下语句解决了这个问题。我正在考虑删除这个问题:CAST(CAST(SUM(hours) as DECIMAL(5,2))/40 AS Decimal(5,2))
  • @Pieter Geerkens,我不明白你的担忧。请在评论中发表。

标签: sql sql-server


【解决方案1】:

你正在做numeric(5,2) / 40。

来自Precision, Scale, and Length

+-----------+------------------------------------+---------------------+
| Operation |          Result precision          |   Result scale *    |
+-----------+------------------------------------+---------------------+
| e1 / e2   | p1 - s1 + s2 + max(6, s1 + p2 + 1) | max(6, s1 + p2 + 1) |
+-----------+------------------------------------+---------------------+

将 40 视为 numeric(2,0),因为这是保留精度和小数位数的最小十进制表示形式。

所以

p1=5
s1=2,
p2=2
s2=0

然后将其插入 BOL 的公式中

Precision: 5 - 2 + 0 + max(6, 2 + 2 + 1) = 9
Scale: max(6, 2 + 2 + 1)                 = 6

所以结果是numeric(9,6)

你也可以从

看到这个
;WITH cte(thing) AS
(
 SELECT CAST(1 as DECIMAL(5,2))/40
)
SELECT thing, 
       sql_variant_property(thing,'basetype') AS basetype,
       sql_variant_property(thing,'precision') AS precision, 
       sql_variant_property(thing,'scale') AS scale, 
       sql_variant_property(thing,'maxlength') AS maxlength
FROM cte

返回

+----------+----------+-----------+-------+-----------+
|  thing   | basetype | precision | scale | maxlength |
+----------+----------+-----------+-------+-----------+
| 0.025000 | decimal  |         9 |     6 |         5 |
+----------+----------+-----------+-------+-----------+

(注意:decimalnumeric 是同义词)

【讨论】:

  • 太棒了,我不知道计算列的精度/比例计算背后有实际的数学。很棒的链接!
  • 你能澄清你的第一行是什么意思,即你正在做数字(5,2)/40吗?我正在转换为 DECIMAL 而不是 NUMERIC。
  • @w0051977 他们是同一个东西。
【解决方案2】:

多么奇怪。如果你运行sp_describe_first_result_set

sp_describe_first_result_set N'
select 
case when sum(hours) > 0 Then
    CAST(SUM(hours) as DECIMAL(5,2))/40
else 0 end as [hours],
[DATE]
from resource group by date'

您看到您返回的小时数列被转换为十进制(9,6)。

如果您将原始转换更改为 DECIMAL(10,6),它将重新转换为 (14,10)。所以你认为它只是增加了 4 个级别的小数精度。不完全是!

将您的分频器从 40.0 更改为 400.0 - 现在它转换为 (15,11) - 它还根据分频器的精度增加了额外的精度级别。

改为 40.0000(3 个额外的零) - 现在是 (20,15)。因此,有一个函数可以根据原始值和除数来确定精度。

小数点右边每增加一个精度级别,就会在您的原始演员表中增加 (2,1)。 左边的每个精度级别都会为您的原始演员添加 (1,1)。

要将小时列返回为小数(5,2),您只需要这样做

select 
case when sum(hours) > 0 Then
    CAST(SUM(hours) /40.0 as decimal(5,2))
else 0 end as [hours],
[DATE]
from resource group by date

【讨论】:

  • 谢谢,这确实有效 +1。我之前尝试了以下 CAST,但这不起作用:CAST(SUM(hours) /40 as decimal(5,2))。为什么将 40 更改为 40.0 会有所不同?
  • 什么是:sp_describe_first_result_set N'?
  • @w0051977:因为“/”是整数除法,除非它的参数之一强制它进行浮点除法。
  • @w0051977 我在答案中添加了一个链接。它是 SQL Server 对 SET FMTONLY ON/OFF 的替代品。
  • @PieterGeerkens 关于这个stackoverflow.com/questions/54780779/…的任何线索@
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
  • 2012-03-23
  • 1970-01-01
  • 1970-01-01
  • 2013-02-03
相关资源
最近更新 更多