列的类型
我们前边说过,MySQL
底层其实把数据存储到了表里边,而表又是由行和列组成的,还是拿我们之前说过的学生基本信息表
做个例子:
学生基本信息表
学号 | 姓名 | 性别 | 身份证号 | 学院 | 专业 | 入学时间 |
---|---|---|---|---|---|---|
20180101 | 杜子腾 | 男 | 158177199901044792 | 计算机学院 | 计算机科学与工程 | 2018-09-01 |
20180102 | 杜琦燕 | 女 | 151008199801178529 | 计算机学院 | 计算机科学与工程 | 2018-09-01 |
20180103 | 范统 | 男 | 17156319980116959X | 计算机学院 | 软件工程 | 2018-09-01 |
20180104 | 史珍香 | 女 | 141992199701078600 | 计算机学院 | 软件工程 | 2018-09-01 |
20180105 | 范剑 | 男 | 181048200008156368 | 航天学院 | 飞行器设计 | 2018-09-01 |
20180106 | 朱逸群 | 男 | 197995199801078445 | 航海学院 | 电子信息 | 2018-09-01 |
表里的一行就代表一个学生的基本信息,这一行中的某一列就代表这个学生基本信息中的一项属性,也就是说学号
是学生的一项属性、姓名
也是学生的一项属性,其他的列也都是这个学生的属性。但是这些属性都有一定格式,比如说学号必须是整数格式的,入学时间必须是日期格式的,其他的属性都是字符串格式的,不同格式的数据是不能随便乱填的,你把一个日期格式的数据填在了性别里,岂不是闹出了笑话。所以设计MySQL
的大叔们针对每一种格式定义了一种相应的类型,我们接下来就要详细唠叨具体有哪些类型以及它们是怎么存储在计算机中的。
注:身份证号由于最后以为可能是X,所以就归为字符串了。
数值类型
整数类型
进制
long long ago,原始人是没有现在的人这么聪明的,只会用十分简单的东西计数。比方说他们只会使用麦秆来统计猎物数量,每收获一个猎物就在麦秆堆里添加一支麦秆,所以如果猎物多的话,麦秆就会累积很多,数都数不过来。后来人们发现不用这么笨,可以把麦秆折叠成不同的形状来代表不同的数量。普通的麦秆就代表1,也就是说每添加一只猎物就多放一只普通麦秆;如果当前已经放了9根普通的麦秆,此时再添加了一只猎物,就用一个心形麦秆来取代之前的9根普通麦秆;假如当前已经有了9根心形麦秆和9根普通麦秆时又添加了一只猎物的话,就用一根矩形麦秆来替代之前所有的麦秆。像这种每逢10个数向前进一位的计数方法就叫做十进制,这样他们就可以用很少的麦秆来表示很大的数字了。
如果在计数的时候每逢8个数就往前进一位就是八进制,每逢9个数就往前进一位就是九进制。生活中常用的进制除了十进制外,还有钟表里用来统计时间的十二进制和六十进制,用在计算机里的二进制和十六进制。 以十进制数字109
为例,它表示有1个10²
,加0个10¹
,加9个10⁰
,用数学符号表示就是:
109 = 1 × 10² + 0 × 10¹ + 9 × 10⁰
复制代码
这个数字也可以这么表示:
109 = 1 × 8² + 5 × 8¹ + 5 × 8⁰
复制代码
也就是从八进制的逢8进一的角度上考虑,这个数可以被表示为155
(八进制)。当然这个数也可以这么写:
209 = 1 × 2⁶ + 1 × 2⁵ + 0 × 2⁴ + 1 × 2³ + 1 × 2² + 0 × 2¹ + 1 × 2⁰
复制代码
也就是从二进制的逢2进一的角度上考虑,这个数也可以被表示为:1101101
(二进制)。又因为计算机中一般用8个位表示一个字节,平时都是用若干个字节来表示一个整数,假如用1个字节表示109
的话,那效果就是这样:01101101
,假如用两个字节表示十进制数19
的话,那效果就是这样:0000000001101101
。
MySQL的整数类型
很显然,使用的字节数越多,意味着能表示的数值范围就越大,但是也就越耗费存储空间。根据表示一个数占用字节数的不同,MySQL
把整数划分成如下所示的类型:
类型 | 字节数 | 无符号数取值范围 | 有符号数取值范围 | 含义 |
---|---|---|---|---|
TINYINT |
1 | 0 ~ 2⁸-1 | -2⁷ ~ 2⁷-1 | 非常小的整数 |
SMALLINT |
2 | 0 ~ 2¹⁶-1 | -2¹⁶ ~ 2¹⁶-1 | 小的整数 |
MEDIUMINT |
3 | 0 ~ 2²⁴-1 | -2²⁴ ~ 2²⁴-1 | 中等大小的整数 |
INT |
4 | 0 ~ 2³²-1 | -2³² ~ 2³²-1 | 标准的整数 |
BIGINT |
8 | 0 ~ 2⁶⁴-1 | -2⁶⁴ ~ 2⁶⁴-1 | 大整数 |
以TINYINT
为例,用1个字节,也就是8个位表示有符号数
的话,就是既可以表示正数,也可以表示负数的话,需要有一个二进制位表示正负号。但是如果表示无符号数
的话,也就是只表示非负数的话,就不需要表示正负号,这是有符号数
和无符号数
的区别。具体每个类型的取值范围是如何计算出来的我们这就不唠叨了,可以找一本计算机基础的书看看。
浮点数类型
用二进制表示十进制小数
浮点数就是小数,我们平时用的的十进制小数也可以被转换成二进制后被计算机存储。比如9.875
,这个小数可以被表示成这样:
9.875 = 8 + 1 + 0.5 + 0.25 + 0.125 = 1 × 2³ + 1 × 2⁰ + 1 × 2⁻¹ + 1 × 2⁻² + 1 × 2⁻³
复制代码
也就是说,如果十进制小数9.875
转换成二进制小数的话就是:1001.111
。为了在计算机里存储这种二进制小数,我们统一把它们表示成a × 2ⁿ
的科学计数法的形式,其中1≤|a|
<2,比如1001.111
可以被表示成1.001111 × 2³
,我们把小数点之后的001111
称为尾数
,把2³
中的3
称为指数
,然后只需要在计算机中的二进制位中表示出尾数
和指数
就行了。用科学技术法来计数的时候,小数的小数点看起来就像移动了一样,所以这样的小数也叫做浮点数
。
当然,如果你需要表示有符号小数
,那还得用一个二进制位来表示正负号。如果我们用四个字节,也就是32个位来表示一个有符号小数的话,我们可以这么划分各个部分所表示的意义:
MySQL的浮点数类型
很显然,使用的字节数越多,表示尾数
和指数
的范围就越大,也就是说表示的小数范围就越大,所以设计MySQL
的大叔根据表示一个小数需要的不同字节数定义了如下的两种类型来表示小数:
类型 | 字节数 | 绝对值最小非0值 | 绝对值最大非0值 | 含义 |
---|---|---|---|---|
FLOAT |
4 | ±1.175494351e-38 | ±3.402823466e38 | 单精度浮点数 |
DOUBLE |
8 | ±2.2250738585072014e-308 | ±1.7976931348623157e308 | 双精度浮点数 |
需要注意的是,虽然有的十进制小数,比如1.875
可以被很容易的转换成二进制数0.111
,但是更多的小数是无法直接转换成二进制的,比如说0.3
,它转换成的二进制小数就是一个无限小数,但是我们现在只能用4个字节或者8个字节来表示这个小数,所以只能进行四舍五入来近似的表示,所以我们说计算机的浮点数表示有时是不精确的。
设置有效位数和小数位数
对于我们用户而言,使用的都是十进制小数。表示一个小数需要的数字个数称为有效位数
,小数点后的数字个数称为小数位数
,11.2
和-0.0021
这两个小数的有效位数和小数位数见下表:
小数 | 有效位数 | 小数位数 |
---|---|---|
11.2 |
3 | 1 |
-0.0021 |
5 | 4 |
如果我们知道某列属性需要的有效位数和小数位数,我们可以用这样的方式手动指定一下:
FLOAT(M, D) 或 DOUBLE(M, D),其中M指`有效位数`,D指`小数位数`
复制代码
举个例子看一下,设置了有效位数和小数位数的单精度浮点数的取值范围的变化:
类型 | 取值范围 |
---|---|
FLOAT(4, 1) |
-999.9~999.9 |
FLOAT(5, 1) |
-9999.9~9999.9 |
FLOAT(6, 1) |
-99999.9~99999.9 |
FLOAT(4, 0) |
-9999~9999 |
FLOAT(4, 1) |
-999.9~999.9 |
FLOAT(4, 2) |
-99.99~90.99 |
可以看到,在小数位数相同的情况下,有效位数越多,该类型的取值范围越大;在有效位数相同的情况下,小数位数越大,该类型的取值范围越小。当然,M
和D
的取值也不是无限大的,你要是把有效位数取成一个亿,那内存不得崩了么,别忘了单精度浮点数只有4个字节的存储空间,双精度浮点数只有8个字节的存储空间,超过了这个存储空间所能表达的小数就无效了。
定点数类型
正因为用浮点数表示小数可能会有不精确的情况,在一些情况下我们必须保证小数是精确的,所以设计MySQL
的大叔们提出了定点数的概念,它也是存储小数的一种方式:
类型 | 字节数 | 取值范围 |
---|---|---|
DECIMAL(M, D) |
取决于M和D | 取决于M和D |
此处的M
指的就是有效位数,D
指的就是小数位数。M
和D
对取值范围的影响我们之前在唠叨浮点数的时候已经介绍过了,但是为啥M
和D
的取值还会影响到需要的字节数呢?
我们说定点数是一种精确的小数,为了达到精确的目的我们不能把它转换成二进制之后再存储(这可能会产生四舍五入的情况)。我们可以以小数点为界,把一个小数看成是两个整数被小数点分隔开来的样子,所以我们可以把一个小数分成3块来存储,一是小数点左边的整数,而是小数点本身,三是小数点右边的整数,这样就可以保证存储的小数肯定是精确的了。
以DECIMAL(6, 1)
为例,这种类型的取值范围是-99999.9~99999.9
。所以整数部分绝对值最大的就是99999
,小数部分最大的就是9
,而整数部分的最大值99999
这个十进制数最少需要用3个字节才能存放,小数部分的最大值9
只需要1个字节就可以存放,所以最后这个定点数类型就需要使用5个字节,各个部分的划分如下:
所以不同的有效位数和小数位数,最终需要的字节数也是不同的。可以看到,与浮点数相比,定点数需要更多的空间来存储数据,所以如果不是像工资一样特重要的数据,一般的小数用浮点数就足够了。
M
和D
都是可选的,默认的M
的值是10,默认的D
的值是0,也就是说下列等式是成立的:
DECIMAL = DECIMAL(10) = DECIMAL(10, 0)
DECIMAL(n) = DECIMAL(n, 0)
复制代码
另外,有效位数和小数位数也不是无限大的(太大了内存受不了),限制M
的范围是1~65
,D
的范围是0~30
,且D
的值不能超过M
。
非负数值类型的表示
对于数值类型,包括整数、浮点数和定点数,有些情况下我们只需要用到非负数,而且对于整数来说,单纯的表示非负数能将正整数的表示范围提升一倍,所以MySQL
给我们提供了一个表示非负数值类型的方式,就是在原数值类型后加一个单词UNSIGNED
:
数值类型 UNSIGNED
复制代码
大家可以把它当成一种新类型对待,比如INT UNSIGNED
就表示非负整数,取值范围是0 ~ 2³²-1
。
字符串类型
字符和字符串
字符
分为两种,一种叫可见字符
,一种叫不可见字符
。顾名思义,可见字符
就是打印出来后能看见的字符。比如a
,b
,我
,。
... 这样的人眼能看见的单个国家文字、标点符号、图形符号、数字等这样的东东,我们就叫做一个可见字符
。不可见字符
也好理解,就是打印机或者在黑框框里打印字符的时候有时候需要换行,打个制表符啥的,或者在输出某个字符的时候就发出嘟
地一声,这种我们看不到,只是为了控制输出效果的字符叫做不可见字符
。字符串
就是把字符连起来的样子,比如abc
,就是由a
、b
、c
三个字符连起来的一个字符串
,下边列举了4个字符串的例子:
'我喜欢你'
'me, too'
'give me a hug'
'么么哒'
复制代码
字符编码简介
在具体分析MySQL
中各个字符串类型之前,我们一定要先搞明白字符和字节的区别。字符是面向人的概念,字节是面向计算机的概念。如果你想在计算机中表示字符,那就需要通过字符编码
来将一个字符映射到一个二进制数据。不幸的是,这种映射关系并不是唯一的,不同的人制作了不同的字符编码,根据表示一个字符使用的字节数是不是相同的,编码方式可以分为下边两种:
- 固定长度的编码采用相同的字节数来表示一个字符,比方说
ASCII
编码方式采用1个字节来表示一个字符,ucs2
采用2个字节来表示一个字符。 - 变长编码 采用长度不一样的字节来表示不同的字符,比方说
utf8
编码方式采用1~3
个字节来编码一个字符,gb2312
采用1~2
个字节来编码一个字符。
对于不同的字符编码方式来说,对同样一个字符可能会产生不一样的编码,比如同样一个字符:我
,在utf8
和gb2312
这两种编码方式下被映射成如下的二进制数据如下:
`utf8`编码:111001101000100010010001 (3个字节,十六进制表示是:0xE68891)
`gb2312`编码:1100111011010010 (2个字节,十六进制表示是:0xCED2)
注:十六进制前边的`0x`是前缀,表示后边的是16进制数据。
复制代码
如果你对上边说的话一知半解,那你需要系统的学习一下字符集的相关概念:字符集和编码介绍。
小贴士: MySQL对`utf8`编码做了限制,它只能使用3个字节去编码字符,如果有的字符需要4个字节的话,可以使用`utfmb4`,这只是出于性能考虑的,毕竟我们的大部分场景中不会使用到4个字节编码的字符。
MySQL的字符串类型
现在我们可以看一下MySQL
中提供的各种字符串类型(注:其中M
代表你要存储的字符串中最多包含的字符数量,L
代表在实际字符串在某个字符编码下所占用的字节数,W
代表某个固定长度编码方式中编码一个字符需要的字节数):
类型 | 最大长度 | 存储空间要求 | 含义 |
---|---|---|---|
CHAR(M) |
M个字符 | L个字节或M个字节或M×W个字节 | 固定长度的字符串 |
VARCHAR(M) |
M个字符 | L+1 或 L+2 个字节 | 可变长度的字符串 |
TINYTEXT |
2⁸-1 个字节 | L+1个字节 | 非常小型的字符串 |
TEXT |
2¹⁶-1 个字节 | L+2 个字节 | 小型的字符串 |
MEDIUMTEXT |
2²⁴-1 个字节 | L+3个字节 | 中等大小的字符串 |
LONGTEXT |
2³²-1 个字节 | L+4个字节 | 大型的字符串 |
CHAR(M)
CHAR(M)
中的M
取值范围是0~255
。如果省略掉M
的值,那它的默认值就是1,也就是说CHAR
和CHAR(1)
是一个意思。再回头看一眼我们的学生基本信息表,如果你觉得学生的姓名不会超过5个字符,你就可以指定这个姓名列的类型为CHAR(5)
。
CHAR(M)
在不同的编码方式下需要的存储空间也是不一样的。假设我们实际存储的字符串在某个编码方式下占用的字节数为L
,则CHAR(M)
实际占用的字节数取决于该编码方式是否是固定长度的以及M
和L
的值,具体计算方式如下:
- 如果该编码方式是固定长度的,
W
代表固定长度的字节数,比方说ASCII
编码方式的W
的值就是1
,ucs2
编码方式的W
的值就是2
,则CHAR(M)
占用的存储空间大小与实际存储的数据无关,都是M×W
个字节。 - 如果该编码方式是变长的,则分为两种情况:
- 如果
L
小于M
,则占用的存储空间大小为M
个字节。 - 如果
L
大于M
,则占用的存储空间大小为L
个字节。
- 如果
比方说我们现在使用的字符串类型为CHAR(5)
,所以现在M
的值就是5
,然后我们看一下CHAR(5)
实际占用的存储空间的情况:
- 如果我们使用固定长度编码
ucs2
进行编码,不管存储的数据是啥,最后占用的存储空间都一样。W
的值是2,所以CHAR(5)
占用的空间就是5×2=10
个字节。 - 如果我们使用变长的
utf8
进行编码。- 假设实际存储的字符串为
我
,因为它用utf8
编码后的结果是0xE68891
,也就是说实际占用空间L
的值为3
,又因为L < M
,所以实际占用的存储空间大小为5
个字节。 - 假设实际存储的字符串为
我我
,因为它用utf8
编码后的结果是0xE68891E68891
,也就是说实际占用空间L
的值为6
,又因为L > M
,所以实际占用的存储空间大小为6
个字节。
- 假设实际存储的字符串为
小贴士: 可以看到,`CHAR(M)`只有在采用固定长度的字符编码时所占用的存储空间大小才是确定的,否则占用存储空间大小与实际字符串占用的字节长度有关。
需要注意的是:如果CHAR(M)
占用的存储空间大于实际字符串需要的存储空间,那多出的存储空间将被空格
填满。这种CHAR(M)
的字符串类型有一个非常不好的地方:一旦你确定了M
的值,如果M
的值很大,而你实际存储的字符串占用字节数又很少,会造成存储空间的浪费。
VARCHAR(M)
如果你表中的某个列需要存储字符串类型的数据,而且这些字符串长短不一,那么使用CHAR(M)
可能造成很大程度上的浪费,VARCHAR(M)
正是为了解决这个问题而生的。
VARCHAR(M)
中的M
也是指你要存储的字符串中最多包含的字符数量,取值范围是1~65535
。但是MySQL
中还有一个规定,就是某一行包含的所有列中存储的数据大小不得超过65535个字节,所以VARCHAR(M)
实际能够容纳的字符数量肯定小于65535。
一个VARCHAR(M)
类型表示的数据由这么两部分组成:
L
,如果L
不大于255,也就是字节数可以用1个字节来表示,那么实际占用的存储空间就是L+1
个字节;如果L
大于255且不大于65535,也就是字节数可以用2个字节来表示,那么实际占用的存储空间就是L+2
个字节;又因为某一行包含的所有列中存储的数据大小不得超过65535个字节,所以L
不可能大于65535。所以实际占用的存储空间只可能是L+1
个字节或者L+2
个字节。我们还用学生的姓名属性做例子,在使用utf8
编码方式的情况下,杜子腾
和范统
这两个字符串可以被编码成如下的样子(二进制太长了,用16进制表示):
`杜子腾`:0xE69D9CE5AD90E885BE (共9个字节)
`范统`:0xE88C83E7BB9F (共6个字节)
复制代码
假设我们给姓名列定义的类型为VARCHAR(5)
,我们看一下这两个字符串的实际存储示意图:
从这个图例也可以看出了,VARCHAR(M)
占用的存储空间大小随着实际存储的内容变化而变化,所以我们说 VARCHAR(M) 是一种可变长度的字符串类型。
各种TEXT类型
虽然VARCHAR(M)
已经可以存储很长的字符串了,可是还是不够咋办?对于很长的字符串,设计MySQL
的大叔们给我们提供了TINYTEXT
、TEXT
、MEDIUMTEXT
、LONGTEXT
四种可以存储大型的字符串的类型。它们也都是变长类型,由实际内容和内容长度构成,因为TINYTEXT
最多可以存储2⁸-1
个字节,所以内容长度用1个字节就可以表示,TEXT
最多可以存储2¹⁶-1
个字节,所以内容长度用2个字节就可以表示,剩下的两个的内容长度占用的字节数也都是按这个规则算出来的。
不是之前有个规定说某一行包含的所有列中存储的数据大小不得超过65535个字节么?那TEXT
、MEDIUMTEXT
、LONGTEXT
这3个类型岂不是不符合规定喽?哈,由于MySQL
的大叔会特别关照这几种类型,所以这几个类型并不在这个规定的限制范围之内。大家如果有什么特别长的文本就可以考虑使用这几个类型了。
ENUM类型和SET类型
视角回到我们的学生信息表,性别一列也需要填写字符串,但是比较特殊的一点是,这一列只能填男
或者女
,填别的字符串就尴尬了!针对这种情况,我们提出了一个叫ENUM
的类型,也称为枚举类型
,它的格式如下:
ENUM('str1', 'str2', 'str3' ⋯)
复制代码
它表示在给定的字符串列表里选择一个。比如我们的性别一列可以定义成ENUM('男', '女')
类型。这个的意思就是性别一列只能在'男'
或者'女'
这两个字符串之间选择一个,相当于一个单选框~
有的时候某一列的值可以在给定的字符串列表中挑选多个,假设学生的基本信息加了一列兴趣
属性,这个属性的值可以从给定的兴趣列表中挑选多个,那我们可以使用SET
类型,它的格式如下:
SET('str1', 'str2', 'str3' ⋯)
复制代码
它表示可以在给定的字符串列表里选择多个。我们的兴趣一列就可以定义成SET('打球', '画画', '扯犊子', '玩游戏')
类型。这个的意思就是兴趣一列可以在给定的这几个字符串中选择一个或多个,相当于一个多选框~效果就像这样:
学号 | 姓名 | ··· | 兴趣 |
---|---|---|---|
20180101 | 杜子腾 | ··· | '打球', '画画' |
20180102 | 杜琦燕 | ··· | '扯犊子' |
20180103 | 范统 | ··· | '扯犊子', '玩游戏' |
20180104 | 史珍香 | ··· | '画画', '扯犊子', '玩游戏' |
ENUM 和 SET 都是一种特殊的字符串类型,在从字符串列表中单选或多选元素的时候用得到它们。
日期与时间类型
我们有很多场景需要表示时间或日期,比如学生基本信息中的入学时间
就需要用日期的格式保存。MySQL
为我们提供了多种关于时间和日期的类型,各种类型能表示的范围如下:
类型 | 存储空间要求 | 取值范围 | 含义 |
---|---|---|---|
YEAR |
1字节 | 1901~2155 | 年份值 |
DATE |
3字节 | '1000-01-01' ~ '9999-12-31' | 日期值 |
TIME |
3字节 | '-838:59:59' ~ '838:59:59' | 时间值 |
DATETIME |
8字节 | '1000-01-01 00:00:00' ~ '9999-12-31 23:59:59' | 日期加时间值 |
TIMESTAMP |
4字节 | '1970-01-01 00:00:01' ~ '2038-01-19 03:14:07' | 时间戳 |
在MySQL5.6.4
这个版本之后,TIME
、DATETIME
、TIMESTAMP
这几种类型添加了对毫秒、微妙的支持。由于毫秒、微秒都不到1秒,所以也被称为小数秒
,MySQL
最多支持6位小数秒的精度,各个位代表的意思如下:
我们可以选择TIME
、DATETIME
、TIMESTAMP
这几种类型最多支持到小数点后几位的时间精度,通用格式为:
类型(小数秒位数)
其中的`小数秒位数`可以在0、1、2、3、4、5、6中选择
复制代码
比如DATETIME(0)
表示精确到秒,DATETIME(3)
表示精确到毫秒,DATETIME(5)
表示精确到10微秒。如果你在选择TIME
、DATETIME
、TIMESTAMP
这几种类型的时候添加了对小数秒的支持,那么存储空间需要相应的扩大,不同的小数秒精度需要的存储空间不同,如下表:
小数秒精度 | 存储空间要求 |
---|---|
0 | 0字节 |
1或2 | 1字节 |
3或4 | 2字节 |
5或6 | 3字节 |
也就是说如果你选择使用DATETIME(1)
,那么需要的存储空间就是在DATETIME
的空间上再加上小数秒需要的空间,就是8+1=9
个字节,类似的,DATETIME(3)
就需要10个字节。所以,MySQL5.6.4
这个版本之后的各个类型需要的存储空间和取值范围就如下:
类型 | 存储空间要求 | 取值范围 | 含义 |
---|---|---|---|
YEAR |
1字节 | 1901~2155 | 年份值 |
DATE |
3字节 | '1000-01-01' ~ '9999-12-31' | 日期值 |
TIME |
3字节+小数秒的存储空间 | '-838:59:59[.000000]' ~ '838:59:59[.000000]' | 时间值 |
DATETIME |
8字节+小数秒的存储空间 | '1000-01-01 00:00:00[.000000]' ~ '9999-12-31 23:59:59'[.999999] | 日期加时间值 |
TIMESTAMP |
4字节+小数秒的存储空间 | '1970-01-01 00:00:01[.000000]' ~ '2038-01-19 03:14:07'[.999999] | 时间戳 |
当然,如果你使用的MySQL
版本还没到5.6.4
,那就不支持小数秒,可以翻上去看原来的存储空间和取值范围。下边我们来详细看一下各种类型。
YEAR
单纯的表示一个年份值而已~不过它只有1个字节大小,所以存储的年份值范围有限,如果我们想存储更多的年份值,可以考虑更换成SMALLINT
(2字节)或者字符串类型啥的~
DATE、TIME和DATETIME
顾名思义,DATE
表示日期,格式是CCYY-MM-DD
;TIME
表示时间,格式是hh:mm:ss[.uuuuuu]
,DATETIME
表示日期+时间,格式是CCYY-MM-DD hh:mm:ss[.uuuuuu]
。其中的CC
、YY
、MM
、DD
、hh
、mm
、ss
、uuuuuu
分别表示世纪、年、月、日、时、分、秒、小数秒。
需要注意的是,DATETIME 中的时间部分表示的是一天内的时间,而 TIME 表示的是一段时间,而且可以表示负值。
TIMESTAMP
1970-01-01 00:00:00
注定是一个特殊的时刻,这一天被称为位零日
,也称为纪元
,MySQL
中把某个时刻距离1970-01-01 00:00:00
的秒数称为时间戳
。比方说当前时间是2018-01-24 11:39:21
,距离1970-01-01 00:00:00
的秒数为1516765161
,那么2018-01-24 11:39:21
这个时刻的时间戳就是1516765161
。不过在MySQL5.6.4
之后,时间戳的值也可以加入小数秒。
用时间戳存储时间的好处就是,它展示的值可以随着时区的变化而变化。比方说我们把2018-01-24 11:39:21
这个时刻存储到一个TIMESTAMP
的列中,那么在中国你看到的时间就是2018-01-24 11:39:21
,如果你去了日本,他们哪里的使用的是东京时间,比北京时间早一个小时,所以他们那显示的就是2018-01-24 10:39:21
。而如果你用DATETIME
存储2018-01-24 11:39:21
的话,那不同时区看到的时间值都是一样的。
MySQL中有设置时区的方式,我们这里先不唠叨,等用到的时候再说啊
复制代码
二进制类型
如果你是专业的程序员的话,肯定对二进制数据不陌生,有时我们也有存储这些二进制数据的需求。MySQL
提供了下边这些数据类型供我们使用:
类型 | 最大长度 | 存储空间要求 | 含义 |
---|---|---|---|
BIT(M) |
M个位 | (M+7)/8个字节 | 固定长度的字符串 |
BINARY(M) |
M个字节 | M个字节 | 固定长度的字符串 |
VARBINARY(M) |
M个字节 | L+1 或 L+2 个字节 | 可变长度的字符串 |
TINYBLOB |
2⁸-1 个字节 | L+1个字节 | 非常小型的字符串 |
BLOB |
2¹⁶-1 个字节 | L+2个字节 | 小型的字符串 |
MEDIUMBLOB |
2²⁴-1 个字节 | L+3个字节 | 中等大小的字符串 |
LONGBLOB |
2³²-1 个字节 | L+4个字节 | 大型的字符串 |
BIT(M)
对于BIT(M)
来说,M
指的是该类型最多能存储的二进制位的个数,比如BIT(3)
就是指最多能存放3个二进制位。而内存空间最少的分配单位就是字节,所以不足一字节的按一字节计算,所以存储空间就是(M+7)/8个字节。
BINARY(M)与VARBINARY(M)
BINARY(M)
和VARBINARY(M)
对应于我们前边提到的CHAR(M)
和VARCHAR(M)
,都是前者是固定长度的类型,后者是可变长度的类型,只不过BINARY(M)
和VARBINARY(M)
是用来存放字节的,而CHAR(M)
和VARCHAR(M)
是用来存储字符的。
其他的二进制类型
TINYBLOB
、BLOB
、MEDIUMBLOB
、LONGBLOB
是针对数据量很大的二进制数据提出的,比如图片、音频、压缩文件啥的。它们很像TINYTEXT
、TEXT
、MEDIUMTEXT
、LONGTEXT
,不过BLOB
是用来存储字节的,而TEXT
是用来存储字符的而已。在通常情况下,我们一般都是只存储个文件路径而已,然后使用操作系统的文件系统去访问文件的~
总结
数据库底层使用表来存放数据的,一张表有很多列,每个列都有可能存放不同格式的数据,不同格式的数据是不能混用的,所以MySQL
提出了许多类型来存储不同格式的数据。
用来存储整数的类型有TINYINT
、SMALLINT
、MEDIUMINT
、INT
、BIGINT
这几种,它们需要的存储空间不同,所以能表示的整数范围也不同。
用来存储小数的有FLOAT
、DOUBLE
、DECIMAL
类型,前两个属于浮点型,后一个属于定点型,浮点型更省存储空间,而定点型更精确,这些表示小数的类型都可以使用(M, D)来指定小数的有效位数和小数位数。
用来存储字符串的有CHAR(M)
、VARCHAR
、TEXT
、MEDIUMTEXT
、LONGTEXT
这几种,它们实际占用的字节存储空间依赖与我们当前使用的字符编码,因为在不同编码下,同一个字符可能被编码成不同长度的字节数据,其中的CHAR(M)
是固定长度的类型,其余集中都是可变长度的类型,真实长度取决于实际的字符串长度。
用来存储时间的有YEAR
、DATE
、TIME
、DATETIME
、TIMESTAMP
这几种类型,需要注意的是,在在MySQL5.6.4
这个版本之后,TIME
、DATETIME
、TIMESTAMP
这几种类型添加了对小数秒的支持,但是存储小数秒又要使用额外的存储空间。另外,TIMESTAMP
这种类型存储的是自1970-01-01 00:00:00
时刻起的秒数,所以在不同时区下会显示不同的时间值。
用来存储二进制数据的有BIT(M)
、BINARY(M)
、VARBINARY(M)
、TINYBLOB
、BLOB
、MEDIUMBLOB
、LONGBLOB
这几种,除了BIT(M)
是以二进制位为单位的以外,其余的类型都是以字节为单位的,并且它们的使用类似字符串中的各种类型,只不过一个是以字符为单位,一个以字节为单位而已~