MySQL数字类型int与tinyint、float与decimal如何选择
最近在准备给开发做一个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的长度。
例如:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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)
|
插入几条数据看一下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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。
占用字节数计算方法 —— 小数和整数分别计算,每9位数占4字节,剩余部分如下表换算:
| Leftover Digits | Number of Bytes |
|---|---|
| 0 | 0 |
| 1–2 | 1 |
| 3–4 | 2 |
| 5–6 | 3 |
| 7–9 | 4 |
比如DECIMAL(18,9),整数部分和小数部分各9位,所以各占4字节,共8bytes
再比如DECIMAL(20,6),整数14位,需要4字节存9位,还需3字节存5位;小数6位,需3字节。共10bytes
(感谢 consatan 在评论区提出文中错误)
比如定义DECIMAL(7,3):
- 能存的数值范围是 -9999.999 ~ 9999.999,占用4个字节
- 123.12 -> 123.120,因为小数点后未满3位,补0
- 123.1245 -> 123.125,小数点只留3位,多余的自动四舍五入截断
- 12345.12 -> 保存失败,因为小数点未满3位,补0变成12345.120,超过了7位。严格模式下报错,非严格模式存成9999.999
MySQL使用FLOAT和DOUBLE来表示近似数值类型,这是因为十进制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慢得多。
|
1
2
3
4
5
6
7
8
9
10
11
|
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位有效数字
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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做除法依旧会产生浮点型,所以特殊情况请考虑使用整型,货币单位使用 分 ,或者除法在最后进行。
参考
- MySQL各数据类型的区别
- MySQL manual Out-of-Range and Overflow Handling
- MySQL FLOAT vs DEC: working with fraction and decimal
- Never use floats for money
本文链接地址:http://seanlook.com/2016/04/29/mysql-numeric-int-float/
MySQL数字类型int与tinyint、float与decimal如何选择的更多相关文章
- MySQL数字类型中的三种常用种类
数字类型 MySQL数字类型按照我的分类方法分为三类:整数类.小数类和数字类. MySQL数字类型之一我所谓的“数字类” 就是指 DECIMAL 和 NUMERIC,它们是同一种类型.它严格的说不是一 ...
- MySQL数字类型学习笔记
数字类型 最近在看<MySQL技术内幕:SQL编程>并做了笔记,所以本博客是一篇笔记类型博客,分享出来方便自己复习,也可以帮助他人 整型 类型 占用空间 最小值(SIGNED) 最大值(S ...
- MySQL varchar类型数据转tinyint类型
在mysql数据库中性别字段以前存的是'男'和'女',使用varchar类型存储的,但是在我mongo库中这个字段使用的是'1'和'0'存储的,在两个库之间的数据转换就很不方便,于是想要统一存储类型, ...
- mysql中的int和tinyint、varchar和char、DateTime和TimeStamp区别
一.int和tinyint的区别 大小: tinyint在mysql中占用1个字节 即: 1 bytes = 8 bit ,一个字节最多可以代表的数据长度是2的8次方:11111111 = 256 在 ...
- oracle 的 nubmer 类型与 C# 的 float double decimal 对应关系
如果先有oracle 数据的情况下,怎么对应到C#中的类型. 在oralce 中 在dba_tab_columns表中, Data_type表示字段类型:Data_length表示字段类型的长度:Da ...
- mysql 数字类型的长度区别
mysql整型bigint.int.mediumint.smallint 和 tinyint的语法介绍,如下: 1.bigint 从 -2^63 (-9223372036854775808) 到 2^ ...
- python打造一个Mysql数字类型注入脚本(1)
前言: 总是想写一个sql注入脚本,但是之前的那些都不行. 这次做好了准备,然后嘿嘿嘿. 准备: sql注入的基础知识 熟悉怎么判断 正文: 思路概念图: 这里我没有限制用户输入,不限制的话可能会 @ ...
- mysql整数类型int后面的长度有什么意义
int 的 SQL-92 同义字为 integer.SQL-92 是数据库的一个标准. int类型的存储大小为4个字节 unsigned(无符号) int 存储范围是 2^4*8 int(M) 中的M ...
- MySql 字段类型对应 Java 实体类型
前言 MySQL Connector/J 对于 MySql 数据类型和 Java 数据类型之间的转换是很灵活的: 一般来讲,任何 MySql 数据类型都可以被转换为一个 java.lang.Strin ...
随机推荐
- PAT 甲级 1044 Shopping in Mars (25 分)(滑动窗口,尺取法,也可二分)
1044 Shopping in Mars (25 分) Shopping in Mars is quite a different experience. The Mars people pay ...
- 【Leetcode_easy】705. Design HashSet
problem 705. Design HashSet 题意: solution1: class MyHashSet { public: /** Initialize your data struct ...
- 第十一章 缓存机制——《跟我学Shiro》
转发地址:https://www.iteye.com/blog/jinnianshilongnian-2029217 跟我学Shiro 目录贴:跟我学Shiro目录贴 Shiro提供了类似于Spri ...
- npm的问题【解决】
1.解决npm下载慢的问题,使用该命令 npm install --registry=https://registry.npm.taobao.org 好处:比起cnpm官网解释的,这个更好,使用cnp ...
- 《Hadoop》大数据技术开发实战学习笔记(二)
搭建Hadoop 2.x分布式集群 1.Hadoop集群角色分配 2.上传Hadoop并解压 在centos01中,将安装文件上传到/opt/softwares/目录,然后解压安装文件到/opt/mo ...
- java 画图 Graphics 文字自动换行
/** * 文字超出限定长度自动换行 * * @param g 画布 * @param font 字体样式 * @param text 文字 * @param widthLength 最大长度 (多少 ...
- 谷歌插件:右键搜使用指南.md
基本功能看插件,说几个自定义搜索的实现 自定义技巧:%s选择的文字或页面或链接,%g转换成GBK字符集,%t转换成BIG5字符集,%p剪贴板内容,%u当前页面域名. 注:这部分内容来自谷歌插件介绍.使 ...
- PLSQL设置文件夹排序并设置颜色
- 用ExtentReports美化你的测试报告
前言 在实际的自动化测试工作中经常会用到一些报告生成工具大概分为两类,一类是测试框架自带的报告生成工具如:JUnit+Ant.TestNG:另一类就是专用报告工具如ReportNG等.这些报告要么在U ...
- SQLSever--基础学习--创建登录用户&创建数据库用户&分配权限
如题,本文简记一下SQL Sever里面登录用户(login)的创建,数据库用户(DBUser)的创建,以及给数据库用户分配权限(Grant). 数据库有三层保护机制: 第一层:登录用户以及登录密码的 ...