如何控制计算列的数据类型?

Posted

技术标签:

【中文标题】如何控制计算列的数据类型?【英文标题】:How do I control the datatype of a computed column? 【发布时间】:2015-01-08 02:43:47 【问题描述】:

使用 SQL Server 2012...

我有两列:

Price [decimal(28,12)]
OustandingShares [decimal(38,3)] -- The 38 is overkill but alas, not my call.

当我执行 ALTER TABLE 时,我得到一个作为 [decimal(38,6)] 的计算列。我需要数据类型为 [decimal(28,12)]。

ALTER TABLE [xyz].MyTable
ADD Mv AS OustandingShares * Price

如何在此计算列上有效地获得 12 位小数?我已经尝试将OutstandingShares 转换为小数点后12 位,并围绕OutstandingShares * Price 进行转换。我得到的唯一结果是 [decimal(28,12)] 处的计算域,其中有六个尾随零。

想法?

【问题讨论】:

【参考方案1】:

修复

这就是你想要的:

CONVERT(DECIMAL(28,12), (
                            CONVERT(DECIMAL(15, 3), [OustandingShares])
                          * CONVERT(DECIMAL(24, 12), [Price])
                         )
       )

用这个测试:

SELECT CONVERT(DECIMAL(28,12),
                 (CONVERT(DECIMAL(24,12), 5304.987781883689)
               * CONVERT(DECIMAL(15,3), 3510.88)));

结果:

18625175.503659806036

原因

由于 SQL Server 关于如何跨各种操作处理精度和缩放的规则,计算被截断。这些规则在 Precision, Scale, and Length 的 MSDN 页面中有详细说明。我们对这个案例感兴趣的细节是:

操作: e1 * e2 结果精度: p1 + p2 + 1 结果量表*: s1 + s2

这里的数据类型是:

十进制(28, 12) 十进制(38, 3)

这应该会导致:

精度 = (28 + 38 + 1) = 67 比例 = 15

但是 DECIMAL 类型的最大长度是 38。那么给出了什么?我们现在需要注意的是,“结果量表”计算附加了一个脚注,即:

* 结果精度和小数位数的绝对最大值为 38。当结果精度大于 38 时,相应的小数位数会减小,以防止结果的整数部分被截断。

所以看来,为了让 Precision 回到 38,它砍掉了 9 个小数位。

这就是我提出的修复方法有效的原因。我将“Scale”值保持不变,因为我们不想截断进入并且扩展它们没有任何作用,因为 SQL Server 将根据需要扩展 Scale。关键在于降低精度,以便不存在截断或至少最小化。

使用DECIMAL(15, 3)DECIMAL(24, 12) 我们应该得到:

精度 = (15 + 24 + 1) = 40 比例 = 15

40 超出了限制,因此减少 2 以降低到 38,这意味着将比例减少 2 使我们真正的“结果比例”为 13,这比我们需要的多 1,甚至会看到。

【讨论】:

感谢您的回答。我担心浮点数精确到小数点后 12 位。您对此有何看法? @jermny :我对此也有一点犹豫,但到目前为止,这是我能找到的扩展有效数字范围的唯一方法。我仍在尝试其他想法,但我想我会先分享这个。但我不确定需要多少关注,因为 FLOAT 通常是准确的,理论上是不精确的,但我认为这应该在规模的远端。 @jermny :我想通了,并在不久前发布了答案,现在已经更新了完整的解释。 这是一篇绝对精彩的帖子。非常感谢!【参考方案2】:

使用cast()convert()。比如:

ALTER TABLE [xyz].MyTable ADD Mv AS cast(OustandingShares * Price as decimal(12, 6)

或者你想要的任何类型。

编辑:

哦,我想我明白了。问题在于计算本身。在这种情况下,请在乘法之前进行转换,这样您就不必依赖 SQL Server 的(神秘)规则来符合十进制类型。

ALTER TABLE [xyz].MyTable
    ADD Mv AS cast(OustandingShares as decimal(28, 12) * cast(Price as decimal(28, 12))

我相信您的情况是计算结果的最大精度超过了允许的阈值,因此比例相应减小。这在page 的底部进行了解释。

【讨论】:

我已经提到我已经尝试过了,但我得到的只是尾随零。另外,我希望它不是 12、6,而是 28、12。 @jermny 。 . .你有尾随数字,因为你有 12 的规模。 “精度”是总位数。 “刻度”是小数点右侧的数字。 (msdn.microsoft.com/en-us/library/ms190476.aspx.) 也许你没有使用正确的类型。 感谢您提供有关术语的信息。我很抱歉。 不幸的是,这仍然不起作用:当我执行 ALTER TABLE [xyz].MyTable ADD Mv as cast((OutstandingShares * Price) as decimal(28, 12)) 它仍然有结果3510.88(股)* 5304.987781883689(价格)= 18625175.503660000000(尾随零)。 我想要 12 位小数。我需要能够表示数字 123,456,789,012.123456789012。

以上是关于如何控制计算列的数据类型?的主要内容,如果未能解决你的问题,请参考以下文章

如何在单个查询中计算不同类型列的流数据帧的统计信息?

计算列的数据类型是啥?

【原】Oracle 如何修改列的数据类型

c#如何修改DataTable里面的特定列的数据类型

如何使用Scala计算Spark中数据框中列的开始索引和结束索引之间的行的平均值?

如果使用 where 条件更改了列的数据类型,如何查找列的记录