最近在准备给开发做一个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使用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慢得多。

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做除法依旧会产生浮点型,所以特殊情况请考虑使用整型,货币单位使用 分 ,或者除法在最后进行。

参考


本文链接地址:http://seanlook.com/2016/04/29/mysql-numeric-int-float/

MySQL数字类型int与tinyint、float与decimal如何选择的更多相关文章

  1. MySQL数字类型中的三种常用种类

    数字类型 MySQL数字类型按照我的分类方法分为三类:整数类.小数类和数字类. MySQL数字类型之一我所谓的“数字类” 就是指 DECIMAL 和 NUMERIC,它们是同一种类型.它严格的说不是一 ...

  2. MySQL数字类型学习笔记

    数字类型 最近在看<MySQL技术内幕:SQL编程>并做了笔记,所以本博客是一篇笔记类型博客,分享出来方便自己复习,也可以帮助他人 整型 类型 占用空间 最小值(SIGNED) 最大值(S ...

  3. MySQL varchar类型数据转tinyint类型

    在mysql数据库中性别字段以前存的是'男'和'女',使用varchar类型存储的,但是在我mongo库中这个字段使用的是'1'和'0'存储的,在两个库之间的数据转换就很不方便,于是想要统一存储类型, ...

  4. mysql中的int和tinyint、varchar和char、DateTime和TimeStamp区别

    一.int和tinyint的区别 大小: tinyint在mysql中占用1个字节 即: 1 bytes = 8 bit ,一个字节最多可以代表的数据长度是2的8次方:11111111 = 256 在 ...

  5. oracle 的 nubmer 类型与 C# 的 float double decimal 对应关系

    如果先有oracle 数据的情况下,怎么对应到C#中的类型. 在oralce 中 在dba_tab_columns表中, Data_type表示字段类型:Data_length表示字段类型的长度:Da ...

  6. mysql 数字类型的长度区别

    mysql整型bigint.int.mediumint.smallint 和 tinyint的语法介绍,如下: 1.bigint 从 -2^63 (-9223372036854775808) 到 2^ ...

  7. python打造一个Mysql数字类型注入脚本(1)

    前言: 总是想写一个sql注入脚本,但是之前的那些都不行. 这次做好了准备,然后嘿嘿嘿. 准备: sql注入的基础知识 熟悉怎么判断 正文: 思路概念图: 这里我没有限制用户输入,不限制的话可能会 @ ...

  8. mysql整数类型int后面的长度有什么意义

    int 的 SQL-92 同义字为 integer.SQL-92 是数据库的一个标准. int类型的存储大小为4个字节 unsigned(无符号) int 存储范围是 2^4*8 int(M) 中的M ...

  9. MySql 字段类型对应 Java 实体类型

    前言 MySQL Connector/J 对于 MySql 数据类型和 Java 数据类型之间的转换是很灵活的: 一般来讲,任何 MySql 数据类型都可以被转换为一个 java.lang.Strin ...

随机推荐

  1. Hive实现WordCount详解

    一.WordCount原理 初学MapReduce编程,WordCount作为入门经典,类似于初学编程时的Hello World.WordCount的逻辑就是给定一个/多个文本,统计出文本中每次单词/ ...

  2. react中,用key值来解决一些奇葩问题

    编辑用户信息,角色信息无法加载到值 改进之后:思路:由于值是设置在state里面的,界面编辑时,会重服务器拉去数据,值也设置在state里面了,但是CheckboxGroup依然不会去渲染选中的值, ...

  3. selenium3 web自动化测试框架 四:Unittest介绍及项目实战中的运用

    unittest介绍及运用,可以参考之前写的文章,除了未结合web自动化演示,基础知识都有了 https://www.cnblogs.com/wuzhiming/p/8858305.html unit ...

  4. CMake生成VS2010工程相对路径和绝对路径问题说明

    CMake生成VS2010工程相对路径和绝对路径问题说明 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 主要是使用CMake生成的VS2010的工程,最好不 ...

  5. 关于PADS的一些概念和实用技巧(一)

    关于PADS的一些概念和实用技巧(一) 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 1. 关于part,CAE Decal,PCB Decal Part ...

  6. Redis ZSet 有序集合

    有序集合类型与集合类型的区别就是他是有序的.有序集合是在集合的基础上为每一个元素关联一个分数,这就让有序集合不仅支持插入,删除,判断元素是否存在等操作外,还支持获取分数最高/最低的前N个元素.有序集合 ...

  7. K8S从入门到放弃系列-(3)部署etcd集群

    摘要:etcd 是k8s集群最重要的组件,用来存储k8s的所有服务信息, etcd 挂了,集群就挂了,我们这里把etcd部署在master三台节点上做高可用,etcd集群采用raft算法选举Leade ...

  8. logback的xml配置文件模板(超详细)

    <?xml version="1.0" encoding="UTF-8" ?> <!-- 在此未说明属性为非必须的,那就表示属性必须设置 -- ...

  9. 人机交互技术 Week 1_人机交互概述

    HCI Week 1_Introduction 18-19学年春夏学期选修了计院万华根老师的人机交互技术课程,老师由于知识产权相关原因不能提供课件,故尽可能对课程内容做详尽的课堂笔记以供复习,如有不妥 ...

  10. 利用Python进行数据分析_Pandas_数据加载、存储与文件格式

    申明:本系列文章是自己在学习<利用Python进行数据分析>这本书的过程中,为了方便后期自己巩固知识而整理. 1 pandas读取文件的解析函数 read_csv 读取带分隔符的数据,默认 ...