MySQL中的DOUBLE与DECIMAL相比

use*_*961 63 mysql double decimal

好的,所以我知道有很多文章说我不应该使用DOUBLE来存储MySQL数据库,否则我最终会遇到棘手的精确错误.关键是我没有设计新的数据库,我要求找到优化现有系统的方法.较新的版本包含783个DOUBLE类型列,其中大多数用于存储金钱或公式来计算金额.

所以我对这个主题的第一个看法是我应该强烈推荐在下一个版本中从DOUBLE到DECIMAL的转换,因为MySQL doc和每个人都这么说.但是后来我找不到任何好的论据来证明这个推荐是正确的,原因有三:

  • 我们不对数据库执行任何计算.所有操作都是使用BigDecimal在Java中完成的,而MySQL仅用作结果的普通存储.
  • DOUBLE提供的15位精度是足够的,因为我们存储的数量主要是2位小数,偶尔小数字的8位十进制数字用于公式参数.
  • 我们有6年的生产记录,由于MySQL方面的精度损失,没有已知的bug问题.

即使通过在18毫米行表上执行操作,如SUM和复数乘法,我也无法执行精度不足的错误.而且我们实际上并没有在生产中做这类事情.我可以通过做类似的事情来显示精确度

SELECT columnName * 1.000000000000000 FROM tableName;

但我想不出办法把它变成第二个十进制数的错误.我在互联网上发现的大多数实际问题都是2005年和更早的论坛条目,我无法在5.0.51 MySQL服务器上重现它们.

因此,只要我们不执行任何我们不打算执行的SQL算术运算,我们应该期望只在DOUBLE列中存储和检索金额吗?

bas*_*sh- 46

实际上它是完全不同的.DOUBLE导致舍入问题.如果你做类似的0.1 + 0.2事情会给你类似的东西0.30000000000000004.我个人不相信使用浮点数学的财务数据.影响可能很小,但谁知道.我宁愿让我所知道的是可靠的数据,而不是近似的数据,特别是当你处理货币价值时.

  • 那么这不是对这个案例的技术性答案,但让我认为最多的是_I个人不会相信使用浮点数学的财务数据.当然,即使我花了很多钱,其他很多人也不会100%信任这些数据一个星期试图在我们的用例中证明它是安全的,他们是正确的怀疑.没有客户对审计的信任确实是一个问题,并且推荐从DOUBLE到DECIMAL的转换是一个很好的论据. (15认同)

bro*_*and 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)
Run Code Online (Sandbox Code Playgroud)

基本上如果你得到一个你得到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)
Run Code Online (Sandbox Code Playgroud)

预期的结果是空集.

因此,只要您不执行任何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)
Run Code Online (Sandbox Code Playgroud)


小智 16

我们刚刚经历过同样的问题,但反过来说.也就是说,我们将美元金额存储为DECIMAL,但现在我们发现,例如,MySQL正在计算值4.389999999993,但是当将其存储到DECIMAL字段时,它将其存储为4.38而不是4.39,就像我们想要的那样它来.因此,虽然DOUBLE可能会导致舍入问题,但似乎DECIMAL也会导致一些截断问题.

  • MySQL手册中的[记录](http://dev.mysql.com/doc/refman/5.5/en/fixed-point-types.html)中的截断行为:"为这样的列分配了更多的值小数点后面的数字超过指定比例允许的数字,该值将转换为该比例.(精确的行为是特定于操作系统的,但通常效果是截断到允许的位数.)"我建议使用[ROUND()]显式指定舍入行为(http://dev.mysql.com/doc/refman/5.5/en/mathematical-functions.html#function_round) (6认同)
  • 我只是尝试了这个:CREATE TABLE IF NOT NOT EXISTS`defined`(`n` decimal(5,2)NOT NULL)ENGINE = InnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_bin; 我插入这样的数据:插入`exact`(`n`)值(4.389999999993); 因为四舍五入而有一个警告.但它像你写的那样存储了4.39而不是4.38.我正在使用mySQL 5.5.28-log (4认同)
  • 如果不使用`exact()`,插入的值将被截断,而不是舍入. (3认同)