web-dev-qa-db-ja.com

MySQLのDOUBLEとDECIMAL

OK、だから、MySQLデータベースにお金を保管するためにDOUBLEを使用してはならない、またはトリッキーな精度のバグになってしまうという記事がたくさんあることを知っています。ポイントは、新しいデータベースを設計するのではなく、既存のシステムを最適化する方法を見つけることです。新しいバージョンには、783のDOUBLE型の列が含まれ、それらのほとんどは、金額を計算するためのお金または数式を格納するために使用されます。

したがって、このテーマに関する私の最初の意見は、次のバージョンではDOUBLEからDECIMALへの変換を強くお勧めするべきだということです。MySQLのドキュメントと皆がそう言っているからです。しかし、次の3つの理由から、この勧告を正当化するための適切な議論を見つけることができませんでした。

  • データベースでは計算を実行しません。すべての操作はBigDecimalを使用してJavaで行われ、MySQLは結果のプレーンストレージとして使用されます。
  • DOUBLEが提供する15桁の精度は、主に2桁の10進数で金額を保存するため十分です。
  • MySQL側の精度が失われたため、バグの既知の問題はなく、実稼働で6年の実績があります。

SUMや複雑な乗算など、18ミリ行のテーブルで操作を実行しても、精度不足のバグを実行できませんでした。そして、私たちは実際にこの種のことを実稼働で行いません。私は次のようなことをすることで失われた精度を示すことができます

SELECT columnName * 1.000000000000000 FROM tableName;

しかし、10進数の2桁目でそれをバグに変える方法がわかりません。インターネットで見つけた実際の問題のほとんどは2005年以前のフォーラムエントリであり、5.0.51 MySQLサーバーでそれらを再現することはできませんでした。

したがって、予定していないSQL算術演算を実行しない限り、金額をDOUBLE列に格納および取得するだけで問題が発生する可能性はありますか?

63
user327961

実際、まったく違います。 DOUBLEは丸めの問題を引き起こします。そして、0.1 + 0.2のようなことをすると、0.30000000000000004のようなものが得られます。個人的には、浮動小数点演算を使用する財務データを信頼しません。影響は小さいかもしれませんが、誰が知っています。特にお金の価値を扱っている場合は、近似されたデータよりも信頼できるデータであることがわかっています。

46
bash-

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ステートメントに記述された浮動小数点値は、内部で表される値と同じではない場合があります。

同じことを10進数で繰り返した場合:

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算術演算を実行しない限り、DOUBLEを使用できますが、それでもDECIMALを使用します。

DECIMALについて注意すべきもう1つの点は、小数部が大きすぎる場合の丸めです。例:

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)
34
broadband

私たちはちょうどこの同じ問題を経験してきましたが、逆の方向に進んでいます。つまり、ドル金額をDECIMALとして保存しますが、たとえば、MySQLが4.389999999993の値を計算していることがわかりましたが、これをDECIMALフィールドに保存すると、希望どおり4.39ではなく4.38として保存されていましたそれに。したがって、DOUBLEは丸めの問題を引き起こす可能性がありますが、DECIMALは切り捨ての問題も引き起こす可能性があるようです。

16
Mark

あなたのコメントから、

税額は小数第4位に丸められ、合計金額は小数第2位に丸められます。

コメントの例を使用すると、400ドルの売り上げが1.47ドルになるケースを予測できます。税前の売上は588.00ドルで、税後の売上は合計で636.51ドル(税で$ 48.51を考慮)です。ただし、$ 0.121275 * 400の売上税は$ 48.52になります。

これは、ペニーの違いを強制するための1つの方法でしたが、考案されました。

IRSには、エラーが特定の金額(メモリが提供される場合は0.50ドル)を下回っても気にしない給与税フォームがあります。

あなたの大きな質問は次のとおりです:特定のレポートがペニーでオフになっている場合、誰も気にしませんか?スペックが言う場合:はい、ペニーに正確であなたはDECIMALに変換する努力をする必要があります。

私は1ペニーのエラーがソフトウェアの欠陥として報告された銀行で働いています。私は(無駄に)ソフトウェア仕様を引用しようとしましたが、このアプリケーションにはこの程度の精度は必要ありませんでした。 (多くの連鎖乗算を実行していました。)ユーザー受け入れテストも指摘しました。 (ソフトウェアは検証され、受け入れられました。)

悲しいかな、時々あなたはただ変換をする必要があります。ただし、A)誰かにとってそれが重要であることを確認してから、B)レポートが指定された程度に正確であることを示すテストを作成することをお勧めします。

0
rajah9

「金額をDOUBLE列に格納および取得することだけから予想される問題はありますか?」

シナリオで丸めエラーが発生することはないと思われます。発生した場合、BigDecimalへの変換によって切り捨てられます。

だから私はノーと言うでしょう。

ただし、将来の変更が問題を引き起こさないという保証はありません。

0
Steve Wellens