UFO ET IT

MySQL의 DOUBLE 대 DECIMAL

ufoet 2020. 11. 24. 20:40
반응형

MySQL의 DOUBLE 대 DECIMAL


좋아, 그래서 나는 MySQL 데이터베이스에 돈을 저장하기 위해 DOUBLE을 사용해서는 안된다는 수많은 기사가 있다는 것을 알고있다. 그렇지 않으면 까다로운 정밀 버그로 끝날 것이다. 요점은 새로운 데이터베이스를 설계하는 것이 아니라 기존 시스템을 최적화하는 방법을 찾아야한다는 것입니다. 최신 버전에는 783 개의 DOUBLE 유형 열이 포함되어 있으며 대부분은 금액을 계산하기 위해 돈이나 수식을 저장하는 데 사용됩니다.

그래서 주제에 대한 나의 첫 의견은 다음 버전에서 DOUBLE에서 DECIMAL 로의 변환을 강력히 권장해야한다는 것입니다. 왜냐하면 MySQL 문서와 모두가 그렇게 말하고 있기 때문입니다. 그러나 다음 세 가지 이유로이 권고를 정당화 할 좋은 주장을 찾을 수 없었습니다.

  • 우리는 데이터베이스에서 어떤 계산도 수행하지 않습니다. 모든 작업은 BigDecimal을 사용하여 Java에서 수행되며 MySQL은 결과를위한 일반 저장소로만 사용됩니다.
  • DOUBLE이 제공하는 15 자리 정밀도는 주로 십진수 2 자리로 금액을 저장하고 수식 인수에 소수 8 자리 숫자를 사용하는 경우가 많기 때문에 충분합니다.
  • 우리는 MySQL 측의 정밀도 손실로 인해 알려진 버그 문제없이 6 년 동안 생산 기록을 가지고 있습니다.

SUM 및 복잡한 곱셈과 같은 18 백만 행 테이블에서 작업을 수행하더라도 정밀도 부족의 버그를 수행 할 수 없었습니다. 그리고 우리는 실제로 이런 종류의 일을 프로덕션에서하지 않습니다. 나는 다음과 같은 일을함으로써 잃어버린 정밀도를 보여줄 수 있습니다.

SELECT columnName * 1.000000000000000 FROM tableName;

하지만 소수점 둘째 자리에서 버그로 바꾸는 방법을 찾을 수 없습니다. 내가 인터넷에서 찾은 대부분의 실제 문제는 2005 년 및 이전 포럼 항목이며 5.0.51 MySQL 서버에서 이들 중 어떤 것도 재현 할 수 없었습니다.

따라서 계획하지 않은 SQL 산술 연산을 수행하지 않는 한 DOUBLE 열에 금액을 저장하고 검색하는 것에서 만 예상해야하는 문제가 있습니까?


사실 꽤 다릅니다. DOUBLE은 반올림 문제를 발생시킵니다. 그리고 같은 0.1 + 0.2일을하면 0.30000000000000004. 나는 개인적으로 부동 소수점 수학을 사용하는 금융 데이터를 신뢰하지 않을 것입니다. 그 영향은 작지만 누가 알겠습니까? 나는 특히 돈 가치를 다룰 때 근사화 된 데이터보다 신뢰할 수있는 데이터라는 것을 알고 싶습니다.


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)

기본적으로 a를 더하면 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)

We have just been going through this same issue, but the other way around. That is, we store dollar amounts as DECIMAL, but now we're finding that, for example, MySQL was calculating a value of 4.389999999993, but when storing this into the DECIMAL field, it was storing it as 4.38 instead of 4.39 like we wanted it to. So, though DOUBLE may cause rounding issues, it seems that DECIMAL can cause some truncating issues as well.


"are there any issue we should expect from only storing and retreiving a money amount in a DOUBLE column ?"

It sounds like no rounding errors can be produced in your scenario and if there were, they would be truncated by the conversion to BigDecimal.

So I would say no.

However, there is no guarantee that some change in the future will not introduce a problem.


From your comments,

the tax amount rounded to the 4th decimal and the total price rounded to the 2nd decimal.

Using the example in the comments, I might foresee a case where you have 400 sales of $1.47. Sales-before-tax would be $588.00, and sales-after-tax would sum to $636.51 (accounting for $48.51 in taxes). However, the sales tax of $0.121275 * 400 would be $48.52.

This was one way, albeit contrived, to force a penny's difference.

I would note that there are payroll tax forms from the IRS where they do not care if an error is below a certain amount (if memory serves, $0.50).

Your big question is: does anybody care if certain reports are off by a penny? If the your specs say: yes, be accurate to the penny, then you should go through the effort to convert to DECIMAL.

I have worked at a bank where a one-penny error was reported as a software defect. I tried (in vain) to cite the software specifications, which did not require this degree of precision for this application. (It was performing many chained multiplications.) I also pointed to the user acceptance test. (The software was verified and accepted.)

Alas, sometimes you just have to make the conversion. But I would encourage you to A) make sure that it's important to someone and then B) write tests to show that your reports are accurate to the degree specified.

참고URL : https://stackoverflow.com/questions/6831217/double-vs-decimal-in-mysql

반응형