引言
或许很多同学都很好奇为什么在数据库里要引入decimal这一种数据类型来表示小数?使用float和double这两种数据类型来表示小数为什么不可以? 那是因为float和double这两种类型可能会出现精度问题 如果本文出现了错误,还请路过的大佬在评论区指出,您的批评是我前进的动力!谢谢!
decimal数据类型
参考文献:https://cloud.tencent.com/developer/article/1559660 DECIMAL从MySQL 5.1引入,列的声明语法是DECIMAL(M,D)。NUMERIC与DECIMAL同义,如果字段类型定义为NUMERIC,则将自动转成DECIMAL。
对于声明语法DECIMAL(M,D),自变量的值范围如下:
M是最大位数(精度),范围是1到65。可不指定,默认值是10。 D是小数点右边的位数(小数位)。范围是0到30,并且不能大于M,可不指定,默认值是0。 例如字段 salary DECIMAL(5,2),能够存储具有五位数字和两位小数的任何值,因此可以存储在salary列中的值的范围是从-999.99到999.99。
# 创建具有DECIMAL字段的表 验证decimal默认是decimal(10,0)
mysql> create table decimal_tb (col1 decimal,col2 decimal(5,2));
Query OK, 0 rows affected (0.04 sec)
mysql> show create table decimal_tb\G
*************************** 1. row ***************************
Table: decimal_tb
Create Table: CREATE TABLE `decimal_tb` (
`col1` decimal(10,0) DEFAULT NULL,
`col2` decimal(5,2) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
# 插入数据测试
# 结论:超出存储范围会报错,小数位不足会自动补0,首位数字为0自动忽略,小数位超出会截断 并按四舍五入处理。
mysql> insert into decimal_tb (col1,col2) values (100,100);
Query OK, 1 row affected (0.05 sec)
mysql> insert into decimal_tb (col2) values (1.23);
Query OK, 1 row affected (0.01 sec)
mysql> insert into decimal_tb (col2) values (10.2);
Query OK, 1 row affected (0.01 sec)
mysql> insert into decimal_tb (col2) values (09.9);
Query OK, 1 row affected (0.01 sec)
mysql> select * from decimal_tb;
+------+--------+
| col1 | col2 |
+------+--------+
| 100 | 100.00 |
| NULL | 1.23 |
| NULL | 10.20 |
| NULL | 9.90 |
+------+--------+
4 rows in set (0.00 sec)
mysql> insert into decimal_tb (col2) values (9999);
ERROR 1264 (22003): Out of range value for column 'col2' at row 1
mysql> insert into decimal_tb (col2) values (12.233);
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> show warnings;
+-------+------+-------------------------------------------+
| Level | Code | Message |
+-------+------+-------------------------------------------+
| Note | 1265 | Data truncated for column 'col2' at row 1 |
+-------+------+-------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into decimal_tb (col2) values (12.2300);
Query OK, 1 row affected (0.01 sec)
# 变量范围测试
# 结论:M范围是1到65,D范围是0到30,且D不大于M
mysql> alter table decimal_tb add column col3 decimal(6,6);
Query OK, 0 rows affected (0.12 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table decimal_tb add column col4 decimal(6,7);
ERROR 1427 (42000): For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column 'col4').
mysql> alter table decimal_tb add column col4 decimal(65,2);
Query OK, 0 rows affected (0.11 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table decimal_tb add column col4 decimal(66,2);
ERROR 1426 (42000): Too-big precision 66 specified for 'col4'. Maximum is 65.
mysql> alter table decimal_tb add column col5 decimal(60,30);
Query OK, 0 rows affected (0.13 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table decimal_tb add column col6 decimal(60,31);
ERROR 1425 (42000): Too big scale 31 specified for column 'col6'. Maximum is 30.
decimal使用总结: 上面的内容从实战出发,介绍了DECIMAL类型的使用方法及注意事项,你可能也知道float、double这些浮点数类型,这两个同样可以存储小数,但这里不过多介绍,只是提醒大家float、double类型无法确保精度,很容易产生误差,特别是在求和计算的时候,所有当存储小数,特别是涉及金额时推荐使用DECIMAL类型。这里总结下使用DECIMAL应该注意的事项:
DECIMAL(M,D)中,M范围是1到65,D范围是0到30。 M默认为10,D默认为0,D不大于M。 DECIMAL(5,2)可存储范围是从-999.99到999.99,超出存储范围会报错。 存储数值时,小数位不足会自动补0,首位数字为0自动忽略。 小数位超出会截断,产生告警,并按四舍五入处理。 使用DECIMAL字段时,建议M,D参数手动指定,并按需分配。
float与double精度问题的探索
为什么说是探索?因为俺其实对于这个问题理解的并不是很透彻。
/* 首先我们新建一张测试表,并设置两个属性num1和num2
数据类型分别是float(10,6)与double(10,6)
第二个参数的意思小数位数是6位*/
mysql> select * from tt where num1=32.213998;
Empty set (0.00 sec)
mysql> create table test (
-> num1 float(10,6),
-> num2 double(10,6)
-> );
Query OK, 0 rows affected, 2 warnings (0.16 sec)
/*接着我们想表格里插入以下数据:
2 2
1.1 1.1
2.11 2.11
32.214 32.214
41.4513 41.4513
5.21452 5.21452
6.214522 6.214522
7.1421457 7.1421457
mysql> insert into test(num1,num2) value(2,2),
-> (1.1,1.1),
-> (2.11,2.11),
-> (32.214,32.214),
-> (41.4513,41.4513),
-> (5.21452,5.21452),
-> (6.214522,6.214522),
-> (7.1421457,7.1421457);
Query OK, 8 rows affected (0.05 sec)
Records: 8 Duplicates: 0 Warnings: 0
/*查看当前的表里的数据*/
mysql> select * from test;
+-----------+-----------+
| num1 | num2 |
+-----------+-----------+
| 2.000000 | 2.000000 |
| 1.100000 | 1.100000 |
| 2.110000 | 2.110000 |
| 32.214001 | 32.214000 |
| 41.451302 | 41.451300 |
| 5.214520 | 5.214520 |
| 6.214522 | 6.214522 |
| 7.142146 | 7.142146 |
+-----------+-----------+
8 rows in set (0.00 sec)
/*我们发现一个很有趣的问题:
对于2 1.1 2.11 等这种小数位数不够6的会往后添加数字使其近似于插入的数据,那如果是decimal类型的呢?
这里我在将表格扩展一列num3属性,使其数据类型为decimal,并向其插入刚才的数据,并显示出来:
mysql> alter table test add num3 decimal(10,6);
Query OK, 0 rows affected (0.31 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> insert into test(num3) value(2),(1.1),(2.11),(32.214),(41.4513),(5.21452),(6.214522),(7.1421457);
Query OK, 8 rows affected, 1 warning (0.10 sec)
Records: 8 Duplicates: 0 Warnings: 1
mysql> select * from test;
+-----------+-----------+-----------+
| num1 | num2 | num3 |
+-----------+-----------+-----------+
| 2.000000 | 2.000000 | NULL |
| 1.100000 | 1.100000 | NULL |
| 2.110000 | 2.110000 | NULL |
| 32.214001 | 32.214000 | NULL |
| 41.451302 | 41.451300 | NULL |
| 5.214520 | 5.214520 | NULL |
| 6.214522 | 6.214522 | NULL |
| 7.142146 | 7.142146 | NULL |
| NULL | NULL | 2.000000 |
| NULL | NULL | 1.100000 |
| NULL | NULL | 2.110000 |
| NULL | NULL | 32.214000 |
| NULL | NULL | 41.451300 |
| NULL | NULL | 5.214520 |
| NULL | NULL | 6.214522 |
| NULL | NULL | 7.142146 |
+-----------+-----------+-----------+
16 rows in set (0.00 sec)
/*
单精度的float类型:我们作如下的一下尝试:
*/
mysql> select * from test where num1=32.214000;
+-----------+-----------+------+
| num1 | num2 | num3 |
+-----------+-----------+------+
| 32.214001 | 32.214000 | NULL |
+-----------+-----------+------+
1 row in set (0.00 sec)
mysql> select * from test where num1=32.214002;
+-----------+-----------+------+
| num1 | num2 | num3 |
+-----------+-----------+------+
| 32.214001 | 32.214000 | NULL |
+-----------+-----------+------+
1 row in set (0.00 sec)
mysql> select * from test where num1=32.213999;
+-----------+-----------+------+
| num1 | num2 | num3 |
+-----------+-----------+------+
| 32.214001 | 32.214000 | NULL |
+-----------+-----------+------+
1 row in set (0.00 sec)
/*
我们发现对于插入的32.214,在float类型下可以使用其近似值查找,这说明这个数在存储时时以近似值的性质存储;虽然浮点数是固定的,但是存在不确切的值
但是在decimal数据类型中就不存在近似值存储这一说:
*/
/*对于数字精度小于设置的精度的时候,float和double会尽可能使用近似值来存取来保证精度,所以这里会出现32.214001与41.451302
但是decimal只会往后自动补0*/
mysql> select * from test where num3=32.214;
+------+------+-----------+
| num1 | num2 | num3 |
+------+------+-----------+
| NULL | NULL | 32.214000 |
+------+------+-----------+
1 row in set (0.00 sec)
mysql> select * from test where num3=32.214000;
+------+------+-----------+
| num1 | num2 | num3 |
+------+------+-----------+
| NULL | NULL | 32.214000 |
+------+------+-----------+
1 row in set (0.00 sec)
mysql> select * from test where num3=32.214001;
Empty set (0.00 sec)
mysql> select * from test where num3=32.214002;
Empty set (0.00 sec)
/*
如果存入的小数精度大于设置精度就如这里的7.1421457
是一个7位小数,就进行截断即四舍五入,这样的数字查找到的是四舍五入后的,并且确切:
*/
mysql> select * from test where num1=7.142146;
+----------+----------+------+
| num1 | num2 | num3 |
+----------+----------+------+
| 7.142146 | 7.142146 | NULL |
+----------+----------+------+
1 row in set (0.00 sec)
mysql> select * from test where num1=7.1421457;
Empty set (0.00 sec)
/*对于decimal类型的数据,存入的是7.1421457,超过了设置的精度。
因此会四舍五入,变成7.142146,但是对于decimal类型的数据
存入数据库的会后都是以字符串的形式从存储,改变当前属性的精度,也不会产生精度的扩展或者截断问题。
例如这里我将num3的精度延长到8:重新读取*/
mysql> alter table test modify num3 decimal(10,8);
Query OK, 16 rows affected (0.78 sec)
Records: 16 Duplicates: 0 Warnings: 0
mysql> select * from test;
+-----------+-----------+-------------+
| num1 | num2 | num3 |
+-----------+-----------+-------------+
| 2.000000 | 2.000000 | NULL |
| 1.100000 | 1.100000 | NULL |
| 2.110000 | 2.110000 | NULL |
| 32.214001 | 32.214000 | NULL |
| 41.451302 | 41.451300 | NULL |
| 5.214520 | 5.214520 | NULL |
| 6.214522 | 6.214522 | NULL |
| 7.142146 | 7.142146 | NULL |
| NULL | NULL | 2.00000000 |
| NULL | NULL | 1.10000000 |
| NULL | NULL | 2.11000000 |
| NULL | NULL | 32.21400000 |
| NULL | NULL | 41.45130000 |
| NULL | NULL | 5.21452000 |
| NULL | NULL | 6.21452200 |
| NULL | NULL | 7.14214600 |
+-----------+-----------+-------------+
16 rows in set (0.00 sec)
/*
我们发现7.142146不再改变,不会返回到原来的7.1421457.
*/
|