MySQL知识树-支持的数据类型
本篇学习笔记的主要内容:
介绍MySQL支持的各种数据类型(常用),并讲解其主要特点。
MySQL支持多种数据类型,主要包括数值类型、日期和时间类型、字符串类型。
数值类型
MySQL的数值类型包括整数类型、浮点数类型、定点数类型、位类型。
整数类型
MySQL支持的整数类型有tinyint、smallint、mediumint、int、bigint(范围从小到大)。
zerofill
我们在定义整数类型时可以在类型名称后面的小括号内指定显示宽度,例如int(5),当插入的数值宽度小于5位时,MySQL会在数值前面填充宽度。对于int类型如果不手动指定宽度,则默认为int(11)。
显示宽度一般是配合zerofill来使用,即当插入的数值位数未达到指定的显示宽度时,缺少几位就会在数值前填充几个0
图1
图1,我们创建表t_1,两个字段分别为id1和id2,都是int类型。其中id2我们指定了显示宽度为5,而id1没有手动指定显示宽度,因此它的显示宽度会取默认值11。
图2
图2,我们向表中插入一条数据后再将其查询出来,虽然现在id1和id2查询出来的数值都是1,但由于id1在定义时没有指定显示宽度,因此在插入数值1后,其前面10位都被填充了宽度。而id2由于指定了显示宽度,因此其面前只有4位被填充宽度。
图3
图4
图3、图4,为了更加直观的看到填充宽度的效果,我们将id1和id2的定义稍作修改,使用zerofill来填充宽度。
图5
图5,在使用了zerofill后,我们可以看到数值前面被0填充宽度的效果。那么我们在进行查询时使用1或00001作为条件可以得到结果吗?
图6
图6,可以看到在使用1或00001作为查询条件时,能查出id2对应的数值。但这里要注意的是在MySQL中实际存储的值仍然是1,而不是00001,因为00001并不是一种整数的表现形式,而是一种字符串的表现形式,下面的图7将证明这个问题。
图7
图7,我们在查询时使用了hex()函数作为对比,可以看到使用hex()函数得到的值是1,假若hex()得到的值是3030303031(字符串1的16进制为31,字符串0的16进制为30),则可以肯定在MySQL中是以00001的字符串形式进行存储的,但很明显这里并不是。
注:hex()函数可以将一个数字或字符串转换为十六进制格式的字符串
对于指定显示宽度的做法,联想到一个问题,在id2定义为int(5)的情况下,如果插入超过显示宽度的值,会怎么样呢?
图8
图8,向id2插入长度为6位的数值111111时,MySQL没有报任何错误也没有将111111截断。因此说明了显示宽度并不会对插入的数值长度产生限制,两者并没有什么关系,除非插入的数值超过了数据类型的范围,见图9。
图9
图9,可以看到虽然插入成功但MySQL有一个警告(当MySQL的SQL Mode为严格模式时,该插入行为将不能够被完成,同时MySQL会报ERROR),我们在将数据查询出来时可以看到MySQL对原本插入的数据进行了截取,保留值为4294967295。
注:int数据类型有符号的最小值为-2147483648,最大值为2147483647,无符号的最小值为0,最大值为4294967295
知识点说明:
其实对于显示宽度来说,只有配合使用了zerofill,显示宽度才有意义,否则就让显示宽度为默认值就可以了。不要以为指定显示宽度会对整数类型的取值范围有什么影响,两者之间没有任何关系,而说到整数类型的取值范围只有unsigned才会对其产生影响。
unsigned
当我们在定义整数类型时使用了zerofill,MySQL会为我们自动对该列再添加unsigned(图3、图4对列添加了zerofill后,再查看表的DDL [数据库定义语句],会发现列多了unsigned,详见图10)。这是因为当使用了zerofill后,插入该列的值就不可能为负数了,因此自动添加unsigned也是理所应当的,同时unsigned也会增加整数类型最大值的取值范围。
图10
auto_increment
整数类型还有一个属性就是auto_increment,而且这个属性还是整数类型特有的。auto_increment的作用就是使列值保持自动增长,auto_increment的值默认从1开始,也可以手动设置其初始值。对被设置为auto_increment的列插入null值时,实际插入的值是该列当前最大值加1(null并不会影响到被设置为auto_increment列的数据插入,列会正常的进行自动增长)。
当一个列被设置为auto_increment时,通常还需要为该列设置not null和primary key(主键,一般被设置为auto_increment的列会作为主键使用,这里只是说一般,也有非主键的情况)。
另外需要提醒的是一张表中最多只能有一个字段被设置为auto_increment。
浮点数和定点数
这两者都是用来表示小数的,浮点数包括float(单精度)、double(双精度),定点数仅为decimal。两者在定义时都可以指定其精度和标度,精度是指一共显示多少位数字(整数位+小数位),标度是指精确到小数点后多少位,表现形式如:decimal(15,2),这里的精度是15位(整数13位,小数2位),标度是2位。
需要说明的是定点数在MySQL内部是以字符串的形式来保存的,属于准确存储,但表现出来的是小数,它比浮点数更精确。
图11
图12
图13
图11,我们创建一张表,字段id的数据类型为decimal(5,2),如图12在向表里插入超过标度的值时,虽然插入成功但是插入时的数据却被截断了,这里发生了四舍五入。
图13我们向表里尝试插入超过精度的值,难道也会发生截断并四舍五入?两个值会分别显示为123.12和124.12吗?从结果来看明显不是,我们的猜测是完全错误的。在超过精度的情况下,虽然插入成功但插入的值却是指定精度和标度下的最大值,例如(5,2)下的最大值为999.99。
若是在SQL Mode严格模式下,上述这些插入操作将不能被执行成功且MySQL会报ERROR。
额外知识点:
单精度和双精度的区别,这两者的区别可别理解为单精度是精确到小数点后一位,而双精度是精确到小数点后两位,这明显是错误的。实际上由于float的有效位数是7位,double的有效位数是16位,因此单精度、双精度其实是指代这里的有效位数。
另外需要注意的是有效位数并不等于精确位数,纵然float可以表示到小数点后7位,但只有前6位是精确的,第7位很可能造成数据误差。而对于double来说只有前15位是精确的,第16位也很可能造成数据误差。
额外知识点:
关于float、double精度丢失的问题,实际上就是被扩展或截断了,究其缘由是因为存取时标度不一致所导致的。在录入数据时若数据的标度与定义列数据类型时设置的标度不一致,则会导致存入时以近似的值来存储,这就造成了我们上面说到的精度丢失。
那在什么情况下float、double的精度不会丢失呢?其实根据上面出问题的情况,我们可以想到当数据标度与类型标度一样时(录入数据的标度与定义列数据类型时设置的标度一致),就不会发生精度丢失。
鉴于此,我们常选用decimal类型,小于等于其标度的数据都能被正确录入,不会发生精度丢失,因为其是将数据以字符串的形式来存进数据库的,这就保证了精确性。但并不是说decimal就不会发生精度丢失,虽然它不会发生精度扩展但却会发生精度截断。例如当录入数据的标度大于列数据类型设置的标度时,依然会发生四舍五入。
虽然我们说decimal将数据以字符串的形式存入数据库,同时又会存在精度截断的问题(四舍五入),看似两者有文字描述上的冲突,其实不然。我们这样来理解:decimal将发生了四舍五入的数据以字符串的形式存入了数据库,但表现出来的是小数(一个是存储形式,一个是表现形式),且这个小数的精度不会再发生变化,而不管是以什么精度来获取这个值,它都是四舍五入后以字符串形式存入时的值。
位类型
位类型指的就是BIT,它是用来存放二进制数据的,bit(1)表示存储长度为1位的二进制数据。
图14
图15
我们对图14的表中插入超过位数的数据,从图15的第二个查询结果集中可以发现数据发生了截断,数值2的二进制是10,3的二进制是11,它们的第二位都被截断了。
在图15的第一个查询结果集中,需要说明的是在MySQL命令行窗口中使用select * from t_bit_test是无法看到我们需要的数据的,你只能看到有两个笑脸被显示出来,那既然bit中存放的是二进制数据,我们就使用bin()函数以二进制的形式来显示它们。
日期时间类型
日期时间类型的主要区别如下:
①如果要用来表示年月日时分秒,一般使用datetime类型;
②如果要用来表示年月日,一般使用date类型;
③如果要表示时分秒,一般使用time类型;
④如果只是表示年份,一般使用year类型,需要注意的是5.5.27版本之前(不包含该版本)year类型有2位和4位格式这两种表示,在5.5.27版本之后2位格式的year已经不再被支持,year类型的值都会以YYYY的格式显示。
year(2)被弃用主要是因为两位的显示宽度使得该类型表示变得模糊、不明确(显示的值仅仅是最后两位数字),因此如果在设计数据库时需要使用year数据类型,而MySQL的版本又是低于5.5.27版本的话,建议将year类型定义为year(4)以避免问题(从5.5.27版本开始若创建表时将year类型定义为year(2),实则MySQL会强制将其定义为year(4)。详见图15)。
若在将低版本的MySQL升至5.5.27以后的版本时需要将这块的数据做下处理;
⑤如果需要经常插入系统当前时间或将时间更新为系统当前时间,一般使用timestamp类型。
图15
图16
图16,我们创建了一张表,列d1的数据类型为timestamp,通过查看表的定义可以看到MySQL自动将timestamp类型列的默认值设置为了current_timestamp,即系统当前时间,另外还加上了on update current_timestamp(当该行的数据发生变化时,该列的值会随系统当前时间被更新,详细效果见图17、18)。
图17
图17,我们为t_dt_test表新增列i1,int类型显示宽度为2,宽度不够时用0填充。
图18
图18中我们先为表插入一条数据,可以看到插入的d1值为2016-10-22 15:25:31,接着我们将i1的值由1修改为2,再看d1发现原本存入的时间被更新了,此时的值为2016-10-22 15:26:27,这就是随系统当前时间戳更新的效果。
---------------------未完待续---------------------