SQL 十进制格式在所有情况下都无法正常工作
SQL Decimal formatting not working properly in all cases
SQL 服务器小数功能未按预期工作。
为了使用示例数据进行测试,我创建了一个 table 并向其中插入了值。
然后,我尝试对这些值进行 运行 十进制函数。
CREATE TABLE TEST_VAL
(
VAL float
)
SELECT * FROM TEST_VAL
输出:
VAL
----------
16704.405
20382.135
2683.135
SELECT CAST(VAL AS DECIMAL(15, 2)) AS NEWVAL
FROM TEST_VAL;
输出:
NEWVAL
-------------
16704.40
20382.13
2683.14
我希望所有 3 个值的格式相同。但是,对于第三个值,它 returns 上限四舍五入值。
如果我们在第一个查询中扩展三个数字的精度,也许可以解释这一点:
16704.4050
20382.1349
2683.1351
将以上每一项四舍五入到小数点后两位,这是对 DECIMAL(10,2)
的强制转换,会产生:
16704.40
20382.13
2683.14
这个有用吗:
select CONVERT(DECIMAL(15,2), ROUND(VAL, 2, 1)) AS NEWVAL
from TEST_VAL;
这是 SQLServer 2012 的演示:DEMO
第一个问题:为什么它们的值不同?
因为它们的类型不同,CAST(VAL as decimal(4,2))
的格式会像 ##.##
而不是 ##.###
所以在你的情况下它会获得上限值。
为什么不使用相同的类型?
CREATE TABLE T
(
[VAL] DECIMAL(8,3)
);
INSERT INTO T ([VAL])
VALUES (16704.405), (20382.135), (2683.135);
SELECT * FROM T
输出:
VAL
-----------
16704.405
20382.135
2683.135
db<>fiddle here
或者你可以施放AS DECIMAL(8, 3)
SELECT CAST(VAL AS DECIMAL(8,3)) AS NEWVAL
FROM T;
这是由于浮点数的性质是不精确的并且是二进制的。但我想演示这是如何工作的。
问题是无法准确表示小数,例如 0.135。作为浮点表示,它通常是这样的:
0.134999999234243423
(请注意,这些数字以及此答案中所有值的表示都是虚构的。它们旨在具有代表性以说明这一点。)
9
的数量其实更大。而后面的数字只是代表。在此表示中,我们不会看到截断值的问题。毕竟 0.1349999
应该舍入到与 0.13499
.
相同的值
在二进制中,这看起来不同:
0.11101000010101 10011 10011 10011 10011 . . .
---------------- --------------
~0.135 "arbitrary" repeating pattern
(注:数值是虚构的!)
即二进制表示的"infinite"部分不是一堆重复的1
或者重复的0
;它有一个模式。这类似于大多数以 10 为基数的数字的倒数。例如,1/7
有一个六位数的重复分量,142857
。我们往往会忘记这一点,因为常见的倒数要么是精确的 (1/5 = 0.2),要么只有一个重复数字 (1/6 = 0.166666...)。 1/7
是第一个不那么简单的情况——几乎所有的小数都是这样。对于有理数,总是一个重复序列,不管基数如何,它永远不会长于被除数(底部的数字)减1)。
我们可以认为这是因为所有十进制表示(不管基数如何)总是有一些重复的数字。对于精确表示,重复部分是 0
。对于其他人来说,它很少是一位数。通常,它是多个数字。描述这一点是一项有趣的数学练习。但重要的是重复部分有 1
s 和 0
s.
现在,发生了什么。一个浮点数由三部分组成:
- 一个量级。这是代表指数的位数。
- 整数部分,即小数点前的数字。
- 整数部分,即小数点后的数字。
(实际上,最后两个是一个整数,但我发现将它们分成两个部分更容易解释这一点。)
两个整数部分只有固定数量的位数可用。这看起来像什么?代表性的模式是这样的:
0.135 0 11101000010101100111001110
1.135 1 11101000010101100111001110
2.135 10 1110100001010110011100111
4.135 100 111010000101011001110011
8.135 1000 11101000010101100111001
16.136 10000 1110100001010110011100
-----------^ part before the decimal
------------------^ part after the decimal
注意:这将省略十进制表示的量级部分。
如您所见,数字从末尾被切掉了。但有时 0
会被截断——因此所表示的值没有变化。有时它是 1
。而且还有变化。
有了这个,您也许可以看到这些值本质上是如何波动的,比如说:
0.135 --> 0.135000000004
1.135 --> 0.135000000004
2.135 --> 0.135000000004
4.135 --> 0.135000000001
8.135 --> 0.135999999997
16.135 --> 0.135999999994
然后这些四舍五入不同,这就是您所看到的。
我把这个小 db<>fiddle 放在一起,所以你可以看到舍入是如何围绕 2 的幂变化的。
SQL 服务器小数功能未按预期工作。
为了使用示例数据进行测试,我创建了一个 table 并向其中插入了值。 然后,我尝试对这些值进行 运行 十进制函数。
CREATE TABLE TEST_VAL
(
VAL float
)
SELECT * FROM TEST_VAL
输出:
VAL
----------
16704.405
20382.135
2683.135
SELECT CAST(VAL AS DECIMAL(15, 2)) AS NEWVAL
FROM TEST_VAL;
输出:
NEWVAL
-------------
16704.40
20382.13
2683.14
我希望所有 3 个值的格式相同。但是,对于第三个值,它 returns 上限四舍五入值。
如果我们在第一个查询中扩展三个数字的精度,也许可以解释这一点:
16704.4050
20382.1349
2683.1351
将以上每一项四舍五入到小数点后两位,这是对 DECIMAL(10,2)
的强制转换,会产生:
16704.40
20382.13
2683.14
这个有用吗:
select CONVERT(DECIMAL(15,2), ROUND(VAL, 2, 1)) AS NEWVAL
from TEST_VAL;
这是 SQLServer 2012 的演示:DEMO
第一个问题:为什么它们的值不同?
因为它们的类型不同,CAST(VAL as decimal(4,2))
的格式会像 ##.##
而不是 ##.###
所以在你的情况下它会获得上限值。
为什么不使用相同的类型?
CREATE TABLE T
(
[VAL] DECIMAL(8,3)
);
INSERT INTO T ([VAL])
VALUES (16704.405), (20382.135), (2683.135);
SELECT * FROM T
输出:
VAL
-----------
16704.405
20382.135
2683.135
db<>fiddle here
或者你可以施放AS DECIMAL(8, 3)
SELECT CAST(VAL AS DECIMAL(8,3)) AS NEWVAL
FROM T;
这是由于浮点数的性质是不精确的并且是二进制的。但我想演示这是如何工作的。
问题是无法准确表示小数,例如 0.135。作为浮点表示,它通常是这样的:
0.134999999234243423
(请注意,这些数字以及此答案中所有值的表示都是虚构的。它们旨在具有代表性以说明这一点。)
9
的数量其实更大。而后面的数字只是代表。在此表示中,我们不会看到截断值的问题。毕竟 0.1349999
应该舍入到与 0.13499
.
在二进制中,这看起来不同:
0.11101000010101 10011 10011 10011 10011 . . .
---------------- --------------
~0.135 "arbitrary" repeating pattern
(注:数值是虚构的!)
即二进制表示的"infinite"部分不是一堆重复的1
或者重复的0
;它有一个模式。这类似于大多数以 10 为基数的数字的倒数。例如,1/7
有一个六位数的重复分量,142857
。我们往往会忘记这一点,因为常见的倒数要么是精确的 (1/5 = 0.2),要么只有一个重复数字 (1/6 = 0.166666...)。 1/7
是第一个不那么简单的情况——几乎所有的小数都是这样。对于有理数,总是一个重复序列,不管基数如何,它永远不会长于被除数(底部的数字)减1)。
我们可以认为这是因为所有十进制表示(不管基数如何)总是有一些重复的数字。对于精确表示,重复部分是 0
。对于其他人来说,它很少是一位数。通常,它是多个数字。描述这一点是一项有趣的数学练习。但重要的是重复部分有 1
s 和 0
s.
现在,发生了什么。一个浮点数由三部分组成:
- 一个量级。这是代表指数的位数。
- 整数部分,即小数点前的数字。
- 整数部分,即小数点后的数字。
(实际上,最后两个是一个整数,但我发现将它们分成两个部分更容易解释这一点。)
两个整数部分只有固定数量的位数可用。这看起来像什么?代表性的模式是这样的:
0.135 0 11101000010101100111001110
1.135 1 11101000010101100111001110
2.135 10 1110100001010110011100111
4.135 100 111010000101011001110011
8.135 1000 11101000010101100111001
16.136 10000 1110100001010110011100
-----------^ part before the decimal
------------------^ part after the decimal
注意:这将省略十进制表示的量级部分。
如您所见,数字从末尾被切掉了。但有时 0
会被截断——因此所表示的值没有变化。有时它是 1
。而且还有变化。
有了这个,您也许可以看到这些值本质上是如何波动的,比如说:
0.135 --> 0.135000000004
1.135 --> 0.135000000004
2.135 --> 0.135000000004
4.135 --> 0.135000000001
8.135 --> 0.135999999997
16.135 --> 0.135999999994
然后这些四舍五入不同,这就是您所看到的。
我把这个小 db<>fiddle 放在一起,所以你可以看到舍入是如何围绕 2 的幂变化的。