MySQL数字类型int与tinyint、float与decimal如何选择

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介:

最近在准备给开发做一个mysql数据库开发规范方面培训,一步一步来,结合在生产环境发现的数据库方面的问题,从几个常用的数据类型说起。

int、tinyint与bigint

它们都是(精确)整型数据类型,但是占用字节数和表达的范围不同。首先没有这个表就说不过去了:

Type Storage Minimum Value Maximum Value
(Bytes) (Signed/Unsigned) (Signed/Unsigned)
TINYINT 1 -128 127
0 255
SMALLINT 2 -32768 32767
0 65535
MEDIUMINT 3 -8388608 8388607
0 16777215
INT 4 -2147483648 2147483647
0 4294967295
BIGINT 8 -9223372036854775808 9223372036854775807
0 18446744073709551615

只需要知道对应类型占多少字节就能推算出范围了,比如int占 4 bytes,即4*8=32bits,大约10位数字,也能理解为什么int默认显示位数是11。

遇到比较多的是tinyint和bigint,tinyint一般用于存放status,type这种数值小的数据,不够用时可能会用smallint。bigint一般用于自增主键。

为了避免数据库被过度设计,布尔、枚举类型也采用tinyint。

还有一点也是经常被提到的关于 int(M) 中M的理解,int型数据无论是int(4)还是int(11),都已经占用了 4 bytes 存储空间,M表示的只是显示宽度(display width, max value 255),并不是定义int的长度。

例如:

mysql> CREATE TABLE `tc_integer` (
  `f_id` bigint(20) PRIMARY KEY AUTO_INCREMENT,
  `f_type` tinyint,
  `f_flag` tinyint(1),
  `f_num` smallint(5) unsigned ZEROFILL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

mysql> desc tc_integer;
+----------------+-------------------------------+------+-----+---------+----------------+
| Field          | Type                          | Null | Key | Default | Extra          |
+----------------+-------------------------------+------+-----+---------+----------------+
| f_id           | bigint(20)                    | NO   | PRI | NULL    | auto_increment |
| f_type         | tinyint(4)                    | YES  |     | NULL    |                |
| f_flag         | tinyint(1)                    | YES  |     | NULL    |                |
| f_num          | smallint(5) unsigned zerofill | YES  |     | NULL    |                |
+----------------+-------------------------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)

插入几条数据看一下:
<!-- more -->

mysql> insert into tc_integer values(1, 1, 1, 1);
Query OK, 1 row affected (0.02 sec)

mysql> insert into tc_integer values(9223372036854775808, 127, 127, 65535);
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> show warnings;
+---------+------+-----------------------------------------------+
| Level   | Code | Message                                       |
+---------+------+-----------------------------------------------+
| Warning | 1264 | Out of range value for column 'f_id' at row 1 |
+---------+------+-----------------------------------------------+
1 row in set (0.00 sec)

mysql> select i.*, length(i.f_flag) as len_flag from tc_integer i;
+---------------------+--------------+---------------+----------------+----------+
| f_id                | f_type       | f_flag        | f_num          | len_flag |
+---------------------+--------------+---------------+----------------+----------+
|                   1 |            1 |             1 |          00001 |        1 |
| 9223372036854775807 |          127 |           127 |          65535 |        3 |
+---------------------+--------------+---------------+----------------+----------+
2 rows in set (0.00 sec)

mysql> select * from tc_integer where f_num=' 01' and f_num=1 and f_num=f_flag;
+------+--------------+---------------+----------------+
| f_id | f_type       | f_flag        | f_num          |
+------+--------------+---------------+----------------+
|    1 |            1 |             1 |          00001 |
+------+--------------+---------------+----------------+
1 row in set (0.00 sec)

上面的实验说明了几个问题:

  • f_id列插入比最大值还大的数,出现warnings,并且最终的值自动变成 9223372036854775807 。这个坑曾经在迁移到阿里RDS时遇到过,他们的迁移工具是java写的,结果我们的主键值大于java INTEGER里面的最大限制,导致 duplicate key问题。

  • f_flag的显示宽度为1,但并不影响更多位数的显示。也证实了tinyint(1)并不像char(1)那样限制存储长度

  • f_num定义成无符号的zerofill类型,能存储的最大数值是65535,而signed才是32767。(当列上使用zerofill时,unsigned会自动加上)

  • zerofill的作用是在显示检索结果的时候,左边用0补齐到display width,实际存储时不补0的,仅作为返回结果meta data的一部分。查询的条件值忽略0和空格

  • length()在numeric类型中作用于char_length()一样,因为字节数已经固定了。

zerofill的使用可能会在复杂join时因为了解不够深入而带来问题,所以最终的结论也很简单:除非极端的特殊需要,尽量不用zerofill,建表时这类int无需指定 (11) 这样的显示宽度。

float与decimal

MySQL使用DECIMAL类型去存储对精度要求比较高的数值,比如金额,也叫定点数,decimal在mysql内存是以字符串存储的。声明语法是DECIMAL(M,D),占用字节 M+2 bytes。M是数字最大位数(精度precision),范围1-65;D是小数点右侧数字个数(标度scale),范围0-30,但不得超过M。

比如定义DECIMAL(7,3)

  • 能存的数值范围是 -9999.999 ~ 9999.999,占用9个字节

  • 123.12 -> 123.120,因为小数点后未满3位,补0

  • 123.1245 -> 123.125,小数点只留3位,多余的自动四舍五入截断

  • 12345.12 -> 保存失败,因为小数点未满3位,补0变成12345.120,超过了7位。严格模式下报错,非严格模式存成9999.999

MySQL使用FLOATDOUBLE来表示近似数值类型,这是因为十进制0.1在电脑里用二进制是无法精确表示的,只能尽可能的接近

单精度浮点数float占4字节,float标准语法允许通过FLOAT(M)的形式指定精度,但是这个精度值M只是决定存储大小: 0-23与默认不指定效果相同,24-53就变成双精度的DOUBLE了。

float还有非MySQL自己实现的非标准语法FLOAT(M,D),代表最多存储M个数字长度,其中小数点后数字个数为D。效果与 DECIMAL(M,D)很相似。

double 和 float 的区别是double精度高,有效数字16位(float精度7位)。但double消耗内存是float的两倍,占8字节,double的运算速度比float慢得多。

msyql> create table tc_float(fid int primary key auto_increment,f_float float, f_float10 float(10), f_float25 float(25), f_float7_3 float(7,3), f_float9_2 float(9,2), f_float30_3 float(30,3), f_decimal9_2 decimal(9,2));

mysql> insert into tc_float(f_float,f_float10,f_float25) values(123456,123456,123456);
mysql> insert into tc_float(f_float,f_float10,f_float25) values(1234567.89,12345.67,1234567.89);
mysql> select * from tc_float;
+-----+----------+-----------+------------+------------+------------+-------------+--------------+
| fid | f_float  | f_float10 | f_float25  | f_float7_3 | f_float9_2 | f_float30_3 | f_decimal9_2 |
+-----+----------+-----------+------------+------------+------------+-------------+--------------+
|   1 |   123456 |    123456 |     123456 | NULL       | NULL       | NULL        | NULL         |
|   2 |  1234570 |   12345.7 | 1234567.89 | NULL       | NULL       | NULL        | NULL         |
+-----+----------+-----------+------------+------------+------------+-------------+--------------+
  • 可以看到float与float(10)是没区别的,float默认能精确到6位有效数字

mysql> insert into tc_float(f_float9_2,f_decimal9_2) values(123456.78,123456.78);
mysql> insert into tc_float(f_float9_2,f_decimal9_2) values(1234567.1,1234567.125);
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> show warnings;
+-------+------+---------------------------------------------------+
| Level | Code | Message                                           |
+-------+------+---------------------------------------------------+
| Note  | 1265 | Data truncated for column 'f_decimal9_2' at row 1 |
+-------+------+---------------------------------------------------+
1 row in set (0.00 sec)

mysql> select * from tc_float;
+-----+----------+-----------+------------+------------+------------+-------------+--------------+
| fid | f_float  | f_float10 | f_float25  | f_float7_3 | f_float9_2 | f_float30_3 | f_decimal9_2 |
+-----+----------+-----------+------------+------------+------------+-------------+--------------+
|   6 | NULL     | NULL      | NULL       | NULL       |  123456.78 | NULL        |    123456.78 |
|   9 | NULL     | NULL      | NULL       | NULL       | 1234567.12 | NULL        |   1234567.13 |
+-----+----------+-----------+------------+------------+------------+-------------+--------------+

mysql> insert into tc_float(f_float7_3) values(12345.1);
ERROR 1264 (22003): Out of range value for column 'f_float7_3' at row 1
  • float(9,2)与decimal(9,2)是很像的,并没有前面提到24位一下6位有效数字的限制

  • 他们俩之间的差别就在精度上,f_float9_2本应该是 1234567.10,结果小数点变成 .12 。f_decimal9_2因为标度为2,所以 .125 四舍五入成 .13

  • 将 12345.1 插入f_float7_3列,因为转成标度3时 12345.100,整个位数大于7,所以 out of range 了

另外在编程中应尽量避免做浮点数的比较,否则可能会导致一些潜在的问题。

坚决不允许使用float去存money,使用decimal更加稳妥,但使用decimal做除法依旧会产生浮点型,所以特殊情况请考虑使用整型,货币单位使用 分 ,或者除法在最后进行。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
7月前
|
Python Windows
[oeasy]python076_int这个词怎么来的_[词根溯源]整数类型_int_integer_touch
本文探讨了“int”一词的起源及其与整数类型的关联。通过词根溯源,揭示“int”来源于“integer”,意为“完整的数”,与零碎的分数相对。同时分析了相关词汇如“tact”(接触)、“touch”(触摸)及衍生词,如“tangential”(切线的)、“intagible”(无形的)和“integral”(完整的、不可或缺的)。文章还结合编程语言特性,解释了Python作为动态类型、强类型语言的特点,并总结了整型变量的概念与意义。最后预告了后续内容,提供了学习资源链接。
192 11
|
7月前
|
存储 C语言 Python
[oeasy]python077_int类型怎么用_整数运算_integer_进制转化_int类
本文主要讲解了Python中`int`类型的应用与特性。首先回顾了`int`词根的溯源,探讨了整型变量的概念及命名规则(如匈牙利命名法)。接着分析了整型变量在内存中的存储位置和地址,并通过`type()`和`id()`函数验证其类型和地址。还介绍了整型变量的运算功能,以及如何通过`int()`函数将字符串转化为整数,支持不同进制间的转换(如二进制转十进制)。此外,文章提及了关键字`del`的使用场景,对比了Python与C语言中`int`的区别,并总结了整型与字符串类型的差异,为后续深入学习奠定基础。
116 1
|
10月前
|
存储 关系型数据库 MySQL
double ,FLOAT还是double(m,n)--深入解析MySQL数据库中双精度浮点数的使用
本文探讨了在MySQL中使用`float`和`double`时指定精度和刻度的影响。对于`float`,指定精度会影响存储大小:0-23位使用4字节单精度存储,24-53位使用8字节双精度存储。而对于`double`,指定精度和刻度对存储空间没有影响,但可以限制数值的输入范围,提高数据的规范性和业务意义。从性能角度看,`float`和`double`的区别不大,但在存储空间和数据输入方面,指定精度和刻度有助于优化和约束。
1468 5
|
12月前
|
Python
[oeasy]python036_数据类型有什么用_type_类型_int_str_查看帮助
本文回顾了Python中`ord()`和`chr()`函数的使用方法,强调了这两个函数互为逆运算:`ord()`通过字符找到对应的序号,`chr()`则通过序号找到对应的字符。文章详细解释了函数参数类型的重要性,即`ord()`需要字符串类型参数,而`chr()`需要整数类型参数。若参数类型错误,则会引发`TypeError`。此外,还介绍了如何使用`type()`函数查询参数类型,并通过示例展示了如何正确使用`ord()`和`chr()`进行转换。最后,强调了在函数调用时正确传递参数类型的重要性。
124 3
【Java基础面试五】、 int类型的数据范围是多少?
这篇文章回答了Java中`int`类型数据的范围是-2^31到2^31-1,并提供了其他基本数据类型的内存占用和数值范围信息。
【Java基础面试五】、 int类型的数据范围是多少?
|
自然语言处理 Go 数据安全/隐私保护
对 int 类型的数据加密,有哪些好的方案?
对 int 类型的数据加密,有哪些好的方案?
189 13
|
存储 数据库
如何在数据库中存储小数:FLOAT、DECIMAL还是BIGINT?
【8月更文挑战第7天】在数据库中存储小数时,需谨慎选择数据类型:FLOAT、DECIMAL 或 BIGINT。FLOAT 存储空间小,适于非关键性小数如温度;但精度有限,可能产生误差。DECIMAL 能精确表示小数,适合货币金额等需要高度准确性的场景,不过占用空间较大。BIGINT 用于整数,若存储小数需额外转换处理。根据精度需求及应用场景选择合适类型至关重要。
643 2
|
存储 SQL 数据库
MySQL设计规约问题之为何推荐用DECIMAL代替FLOAT和DOUBLE来存储精确浮点数
MySQL设计规约问题之为何推荐用DECIMAL代替FLOAT和DOUBLE来存储精确浮点数
|
20天前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
56 3
|
26天前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。

推荐镜像

更多