上一篇刚介绍了MySQL的浮点数类型,遗留了一个问题,即当数据需要精确存储的时候怎么办?MySQL既然是面向数据管理的,这点肯定是高瞻远瞩,早就帮我们解决啦,这就是小数的另一种数据类型,称之为定点数,即DECIMAL
。
有的朋友会说,哎不就是DECIMAL
嘛,虽然不知道叫定点数,但我很常用的,这简单,有啥好介绍的。然而这里的设计可是有大文章的,不信,让我们一起探究下DECIMAL的前世今生吧。
定点数是什么?
维基百科上是这么说的:
定点数(英语:fixed-point number)是一种实数数据类型,要求小数点后位数固定,有时也要求小数点前位数固定。定点数与更复杂的浮点数相对。
有木有发现有点绕,其实简单来说就是用一种方式来精确表示小数,当然,如果插入的小数所需的长度大于设定的长度,是会报错的。
mysql> SHOW CREATE TABLE test_decimal\G
*************************** 1. row ***************************
Table: test_decimal
Create Table: CREATE TABLE `test_decimal` (
`d` decimal(3,1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> INSERT INTO test_decimal VALUES (100.2356);
ERROR 1264 (22003): Out of range value for column 'd' at row 1
而如果是精度超过了,则会提示warning,在内部存储进行四舍五入。
mysql> INSERT INTO test_decimal VALUES (1.26);
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> select * from test_decimal;
+------+
| d |
+------+
| 1.3 |
+------+
1 row in set (0.01 sec)
定点数的存储大小是固定的嘛?
要回答这个问题,需要先看MySQL中定点数的定义:
类型 | 占用的存储空间(单位:字节) | 取值范围 |
---|---|---|
DECIMAL(M, D) | 取决于M 和D | 取决于M 和D |
哎,你发现这不是和浮点数很像嘛,如果不填M
和D
,默认就是10 和 0,那为什么这里占用的存储空间不是固定的,而是取决于M
和D
?难道是因为他有别的计算方式?是的,就是因为计算的方式变了,所以存储空间也不一样了。
那它如何表示来确保精度呢?因为在小数从十进制转换为二进制可能会丢失精度,所以就不转了,直接用小数点两边的两个整数搭配就可以了,牛逼吧!因为整数从十进制转换为二进制不会丢失精度,所以这就确保了这种方式不会丢失精度。
这里再提示下,M
和D
的定义:
M
表示该小数最多需要的十进制有效个数。
D
表示该小数的小数点后的十进制数字个数。
那怎么存储呢?
我们用个例子来解释下。对于给定M
、D
值的DECIMAL(M, D)
类型,比如DECIMAL(16, 4)
来说,小数点左边最多需要存储长度为12的十进制数,小数点右边最多需要存长度为4的十进制数。
1、划分组
从小数点位置出发,每个整数每隔9个十进制位划分为1组,效果就是这样的:
可以看到当两边的大小不足9的时候,也会划分为一组。
2、计算组的存储空间
此时分组内还是十进制数,我们要把它转换成二进制数进行存储,组内数目不同,所需的空间也不同,对应关系如下表:
所以DECIMAL(16, 4)
需要的存储空间为2 + 4 + 2 = 8 字节,同时转换完后最高位需要设置为1。而负数形式就先对数字转换之后对每个位取反即可,颇有补码的操作风范(唯一不同的就是取反后无需加1)!
从上边的叙述种我们可以知道,对于DECIMAL(M,D)
类型来说,给定的M和D的值不同,所需的存储空间大小也不同。可以看到,与浮点数相比,定点数需要更多的空间来存储数据,所以如果不是在某些需要存储精确小数点的场景下,一般的小数用浮点数表示就足够了。
另外M的范围是1-65,D的范围是0-30,且D的值不能超过M。
——来自小册《MySQL 是怎样使用的:从零蛋开始学习 MySQL》
好啦,今天的内容就到这啦,你学会(fei)了吗?