好的,所以我知道有很多篇文章说我不应该使用DOUBLE在MySQL数据库上存钱,否则我最终会遇到棘手的精确错误。关键是我没有设计新的数据库,我要求找到优化现有系统的方法。较新版本包含783个DOUBLE类型列,其中大部分用于存储金额或公式来计算金额。
所以我对这个主题的第一个看法是我应该强烈建议在下一个版本中从DOUBLE到DECIMAL的转换,因为MySQL doc和每个人都这么说。但是,由于三个原因,我找不到任何好的论据来证明这一推荐是正确的:
即使在18毫米行表上执行操作,如SUM和复数乘法,我也无法执行精度不足的错误。而且我们实际上并没有在生产中做这类事情。我可以通过做
这样的事情来显示精度损失 SELECT columnName * 1.000000000000000 FROM tableName;
但是我想不出办法把它变成第二个十进制数的错误。我在互联网上发现的大多数实际问题都是2005年和更早的论坛条目,我无法在5.0.51 MySQL服务器上重现它们。
因此,只要我们不执行任何我们不打算执行的SQL算术运算,我们应该期望只在DOUBLE列中存储和检索金额吗?
答案 0 :(得分:42)
实际上它完全不同。 DOUBLE导致舍入问题。如果您执行0.1 + 0.2
之类的操作,它会为您提供0.30000000000000004
之类的内容。我个人不相信使用浮点数学的财务数据。影响可能很小,但谁知道。我宁愿让我所知道的是可靠的数据而不是近似的数据,特别是当你处理货币价值时。
答案 1 :(得分:34)
来自MySQL文档http://dev.mysql.com/doc/refman/5.1/en/problems-with-float.html的示例(我收缩它,本节的文档与5.5相同)
mysql> create table t1 (i int, d1 double, d2 double);
mysql> insert into t1 values (2, 0.00 , 0.00),
(2, -13.20, 0.00),
(2, 59.60 , 46.40),
(2, 30.40 , 30.40);
mysql> select
i,
sum(d1) as a,
sum(d2) as b
from
t1
group by
i
having a <> b; -- a != b
+------+-------------------+------+
| i | a | b |
+------+-------------------+------+
| 2 | 76.80000000000001 | 76.8 |
+------+-------------------+------+
1 row in set (0.00 sec)
基本上如果你总和得到0-13.2 + 59.6 + 30.4 = 76.8。如果我们总结b,我们得到0 + 0 + 46.4 + 30.4 = 76.8。 a和b的总和是相同的,但MySQL文档说:
SQL语句中写入的浮点值可能与内部表示的值不同。
如果我们用十进制重复相同的事情:
mysql> create table t2 (i int, d1 decimal(60,30), d2 decimal(60,30));
Query OK, 0 rows affected (0.09 sec)
mysql> insert into t2 values (2, 0.00 , 0.00),
(2, -13.20, 0.00),
(2, 59.60 , 46.40),
(2, 30.40 , 30.40);
Query OK, 4 rows affected (0.07 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select
i,
sum(d1) as a,
sum(d2) as b
from
t2
group by
i
having a <> b;
Empty set (0.00 sec)
预期结果为空集。
因此,只要您不执行任何SQL arithemetic操作,您就可以使用DOUBLE,但我仍然更喜欢DECIMAL。
关于DECIMAL的另一个注意事项是如果小数部分太大则会四舍五入。例如:
mysql> create table t3 (d decimal(5,2));
Query OK, 0 rows affected (0.07 sec)
mysql> insert into t3 (d) values(34.432);
Query OK, 1 row affected, 1 warning (0.10 sec)
mysql> show warnings;
+-------+------+----------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------+
| Note | 1265 | Data truncated for column 'd' at row 1 |
+-------+------+----------------------------------------+
1 row in set (0.00 sec)
mysql> select * from t3;
+-------+
| d |
+-------+
| 34.43 |
+-------+
1 row in set (0.00 sec)
答案 2 :(得分:16)
我们刚刚经历过同样的问题,但反过来说。也就是说,我们将美元金额存储为DECIMAL,但现在我们发现,例如,MySQL正在计算值4.389999999993,但是当将其存储到DECIMAL字段时,它将其存储为4.38而不是4.39,就像我们想要的那样它来。因此,尽管DOUBLE可能会导致舍入问题,但似乎DECIMAL也会导致一些截断问题。
答案 3 :(得分:0)
“我们应该期待只在DOUBLE列中存储和收回金额吗?”
听起来您的方案中不会产生舍入错误,如果有,它们将被转换为BigDecimal而被截断。
所以我会说不。
但是,无法保证未来的某些变化不会引发问题。
答案 4 :(得分:0)
根据您的评论,
税额四舍五入到小数点后四位,总价格四舍五入 到小数点后2位。
使用评论中的示例,我可以预见一个案例,你有400美元的销售额为1.47美元。税前销售额为588.00美元,税后销售额为636.51美元(税收 $ 48.51 )。但是,0.121275 * 400的销售税将 $ 48.52。
这是一种方法,虽然是人为的,但却强迫一分钱差价。
我会注意到美国国税局有工资税表,如果错误低于一定数额(如果存储服务,0.50美元),他们不在乎。
你的一个大问题是:如果某些报告被一分钱关闭,有人会关心吗?如果您的规格说明:是的,准确到一分钱,那么你应该经过努力转换到DECIMAL。
我曾在一家银行工作过,其中一分钱的错误被报告为软件缺陷。我试图(徒劳地)引用软件规范,这不需要这种应用程序的精度。 (它执行了许多链式乘法。)我还指出了用户验收测试。 (该软件已经过验证和接受。)
唉,有时你只需进行转换。但我鼓励你A)确保对某人很重要,然后B)编写测试表明你的报告准确到指定的程度。