【MySQL】数据类型

序言

在上篇文章中提及了表的结构的操作,不过关于表的创建部分,尤其是数据类型这一方面该如何设计并没有提及,本篇文章就来补充这方面的知识。首先要明确为啥会有数据类型,在计算机的世界中本质虽然都是二进制,但为了与现实世界接轨,方便进行识别与处理各种各样的数据,比如小数,整数,字符串,视频音频图形等二进制数据,出现了与之对应的数据类型。其次数据类型可以对数据进一步的限制,比如可以规定数据的大小,存储和读取的方式,以及表示的范围等。甚至相同类型还可以进一步的划分出范围更小类型,以用来减少空间的消耗。总而言之,在数据库中数据类型可以帮助我们更好的丰富表结构。

一、理论

此部分对常见的数据类型进行罗列和基本说明,方便在使用时进行查看,而对加粗的部分,则为本篇所要重点强调的部分,会在下文进行详细地举例和展示。

数值类型

  • 比特位,bit(M),M用于设置比特位,范围为1~8,即最大表示64。MySQL中以ASCII码的形式显示出来。

  • 整形

    • tinyint,占用一字节。
    • smallint,占用两字节。
    • mediumint,占用三字节。
    • int,占用四字节。
    • bigint,占用八字节。
    • 说明:默认是有符号数的,无符号数得在手动设置时,手动在后面跟上unsigned。
  • 浮点数

    • float(M,D),占用四个字节。
    • double(M,D),占用八个字节。
    • decimal(M,D) ,大小取决于定义的M和D,数学公式为 ⌈ M 2 ⌉ \lceil{ {M} \over {2}} \rceil ⌈2M⌉。
    • 说明:M指的是数据的总长度(不包含小数点),D指的是小数位的长度。默认是有符号的,无符号需手动设置。
  • 布尔,bool,使用0表示假,1表示真。在MySQL中实际使用tinyint(1)代替,1表示的是显示时数据的宽度,即显示1位。

文本,二进制类型

  • 定长字符串,char(L),L表示长度,最长为255。
  • 变长字符串,varchar(L),L表示长度,最大字节数为65535。
  • 二进制数据,blob,最多存储65535字节,即64KB。还有与之类似的tinyblob,mediunblob,longblob对应不同容量的需求。
  • 大文本数据,text,最多存储65535字节,即64KB。还有对应不同容量需求的类型,与int,blob雷同。

时间类型

  • 年月日,date ,数据的格式为Y-M-D,以字符串的形式插入,通常要带上'',占用三个字节。
  • 年月日时分秒,datetime ,数据的格式为Y-M-D-H-M-S,同上,占用八个字节。
  • 时间戳,datestamp,一般采用的是从格林1970年1月1日00:00:00开始到当前时间的秒数,占用四个字节。

枚举类型

  • 枚举其中一个,enum(E),E表示的是一个集合,即可能被选择的所有情况。
  • 至少 枚举其中一个,set(E),同理。

总的来说,分为数值,文本和二进制,时间,枚举共四种类型。除此之外还是要再补充一点,即在MySQL中类型名与sql语句中的关键字一样都是不区分大小写的,只是个人认为小写方便阅读,所以上述呈现的都是小写的,具体如何写根据个人喜好来即可。

二、实践

相信各位读者看完上文的部分之后,已经对数据类型有了一个整理的框架和基本的认识,那将在下面填充具体的使用细节,进一步完善与加深对数据类型的理解。

1.数值

  • tinyint

首先创建一张表,其中存放一个tinyint类型的数据,然后向这种表中全列插入 − 128 , 0 , 127 -128,0,127 −128,0,127,查看表中的数据。过程如下图。

通过上图验证可以大致得到两个结论:tinyint默认是有符号的;tinyint大小是一个字节,即范围为 [ − 128 , 127 ] [-128,127] [−128,127],因插入边界值未出错。


然后再次向表中再次插入数据 − 129 , 128 -129,128 −129,128,观察插入的数据不正常会导致什么效果。

通过以上的三条sql语句可以得知:sql语句的执行是按照一定的顺序运行的,当执行到某句出错就停止了;其次插入非法的值会报错,在一定程度上反映了数据类型具有限制数据范围的作用。


先将表中的数据清空,查看是否清空,然后修改表结构中元素的类型为tinyint unsigned,查看修改后的表结构。过程如下图。

说明:tinyint 改为 unsigned tinyint,数据范围呈现的不是包含的关系,因此改动时需要处理表中的数据,这里处理的动作是清空数据。

修改为unsigned类型之后,只改变了数据的范围到[0,255],其余的属性并没有发生变化。下面插入 128 , 255 128,255 128,255简单地进行验证。

除此之外要简单验证数据范围是否准确,还需插入 − 1 , 256 -1,256 −1,256两个非法的值。如下图。

总而言之,上述实验强调的是当插入的数据大于规定的范围时,MySQL会以错误的形式进行呈现,从而限制和通知用户要插入合法的数据,这其实可以被称为一种简单的约束。在下一篇文章中,将详细地展开讨论表的约束这一话题,嘿嘿,挖了一个坑。

  • bit

先添加一个bit类型的数据,然后把unsigned int类型的变量从上述表结构中移除出去, 因为表中至少要有一种数据,如果这样可行的话就相当于把表删除的活抢了,drop table 可不愿意哦!如下图。

说明:表结构中只有一种数据类型进行删除报错的效果,这里就不做演示了,感兴趣可以将上述两个alter执行的次序倒过来即可查看效果。除此之外,细心一点还可以得到插入bit类型时,没有标明位数默认是1。


然后插入两个数据 0 , 1 0,1 0,1,哈哈,好像也只能插入两位,然后查看显示效果,如下图所示。

可以看出,bit类型的数据在博主的ubuntu下是按照16进制的方式进行显示的,不同的机器可能显示的效果不一样,具体情况,具体分析即可,不用做过多的纠结。

说明:bit类型的是无符号的数据,不能插入负数。除此之外,超出边界的数据的测试,感到有些冗余就不再演示了,感兴趣可自行验证。


将bit的位数增加到8位之后,再插入数据 16 , 32 , 64 16,32,64 16,32,64,查看效果,如下图所示。

总而言之,bit将一个字节的数据以bit为单位再次进行了划分,其目的是进一步提高空间的利用率,但是在不同的操作系统下针对bit类型的实现细节可能不相同,比如ubuntu下,是16进制显示,而Centos下,是ASCII码显示,这会让不方便程序员进行浏览。因此根据实际情况进行权衡,在对资源利用度要求较高的场景考虑,而不要强硬的追求。

  • float

将此表结构的数据清空,增加一个数据长度为4,小数长度为2,名为fnum的float类型,然后将之前的bit类型删除,方便之后进行测试,具体过程如下图。

因为默认是有符号的,再根据数据总长度和小数长度,可以推断数据范围为 [ − 99.99 , 99.99 ] [-99.99,99.99] [−99.99,99.99],那么插入 − 99.99 , 0 , 99.99 -99.99,0,99.99 −99.99,0,99.99三个代表性的数据,然后查看效果,如下图。

除此之外,小数一般来说还有一个四舍五入的特点,因此插入数字的小数位可以不止两位,但因为要求两位小数,所以最终在表中的数据呈现的是两位小数,这其实也算是一种小小的约束。那么就可以再插入 − 99.994 , 0.005 , 0.006 , 99.994 -99.994,0.005,0.006,99.994 −99.994,0.005,0.006,99.994进行验证。

细心的同学就发现了,在进行四舍五入时, 0.005 0.005 0.005实际上显示的还是 0.00 0.00 0.00,而不是我们想的 0.01 0.01 0.01,但是对于 0.006 0.006 0.006来说却遵循着四舍五入的玩法,其实这是由于精度丢失的原因,即在二进制的计算机中要表示十进制的小数会存在一定程度的误差。因此可以得出一个结论,即float并不适用于高精度的场景,而下面所要举例的decimal就可以对此场景做一个很好的补充说明。

补充:对于无符号的,数据总长度为4,小数位为2的float。因为有数据总长度的限制,只能表示 [ 0.00 , 99.99 ] [0.00,99.99] [0.00,99.99]之间的数据,相当于有一半的空间被浪费掉了,因此使用无符号时需要使用者考虑到这一点,同理感觉冗余此处就不做演示了。

  • decimal

将表数据清空,将上述float类型改为16位总长度,12位小数长度的,然后添加与此相同数据格式的decimal,如下图。

然后插入 9876.543210987382 9876.543210987382 9876.543210987382,16位总长度,12位小数长度的小数,对比查看效果。

可见,float在小数点后两位就开始精度丢失了,而decimal则到最后一位还依旧保持坚挺。因此可以得出大致这么一个结论,即在应用场景小数范围,即小数点后两位都在两位之内的话使用float,超出两位的使用decimal

2.文本、二进制

  • char

首先创建一个str_test表,里面先放一个长度为2,且为char类型的变量ch,如下图所示。

然后插入数据"nb","你好",看具体的产生的效果。

简单的讨论一下字符,字节,长度之间的关系,一个英文字符和一个汉字字符的长度都为1,在utf8编码下的一个英文字符对应着一个字节,一个汉字字符对应着三个字节。总而言之,长度是对语言单个字符的泛化,让使用者不用再刻意在意每种语言一个字符的字节数。所以上图中的"你好","nb"长度都为2,注意不要跟字节数搞混了。因此插入"abc"长度为3的字符串是会报错的,如下图所示。

  • varchar

先清空数据,然后添加一个长度为6,varchar类型的变量vch,接着将上述的讨论过的类型从表结构中移除,如下图所示。

请注意varchar的最大字节数为65535,而最大长度则要根据选择的字符集情况进行计算,实际上varchar中还要使用 3 3 3个字节表示字符串的属性信息,方便读取时解析,因此可用的最大字节数为65532。

  • 在UTF-8mb3编码下一个字符最大为3个字节,所以最大长度为 65532 ÷ 3 = 21844 65532 \div 3 = 21844 65532÷3=21844。
  • 在GBK编码下一个字符的最大为2个字节,所以最大长度为 65532 ÷ 2 = 32766 65532 \div 2 = 32766 65532÷2=32766。

在下图中将分别使用UTF-8mb3编码和GBK编码进行实验,首先查看当前的表结构中的字符集,此处为utf8编码,然后将varchar的最大长度先后修改为 21844 , 21845 21844,21845 21844,21845查看结果。

很显然超出边界时会报错,且可证明 21844 21844 21844是utf8编码下的varchar的最大长度。下面将表结构的字符集改为gbk编码,然后同理将最大长度先后修改为 32766 , 32767 32766,32767 32766,32767查看效果,如下图。

同理,此处就不过多赘述了,总而言之,所强调的只有两点:不同字符集下varchar的最大长度可能不同;varchar需要额外的三个字节存放字符串的长度和控制信息。

  • 对比
  1. char是定长字符串,即规定出字符串的最大长度,插入的字符串的大小不管多长都是固定。数据的长度都固定时比较适用,比如身份证号,学号等,且方便存取,直接截取固定长度的数据即可。但如果不固定且只有数据的长度比较分散时,则空间的利用率比较低,不太适用。
  2. varchar是变长字符串,规定出字符串的最大长度,大小取决于实际插入的字符串的大小,不固定,即变长存储。适用数据长度都是比较混乱的情况,可以有效提高空间利用率。但是变长字符串存储在取数据时需要截取出属性字段进行计算解析出数据,因此读取效率较低。

3.日期

说明:日期此处涉及的不多就一并介绍了,date为表示从年到日的时间,datetime表示从年到秒的时间,timestamp表示时间戳。

首先创建一个date_test的表结构,其中添加t1,t2,t3变量类型分别date,datetime,timestamp,过程如下图。

然后插入与之对应的日期数据,时间戳可使用系统命令 date +%s进行查看,同时注意date, datetime插入时应以字符串的形式,即数据要在两边加上'',其次时间戳插入要使用from_unixtime()进行转换,具体过程如下图。

此处关于时间戳还有一个更有意思的玩法,不过涉及到表的约束,就放到下一篇文章中了,嘿嘿又挖一个坑。

4.枚举

  • enum

众所周知,我有一位故人会唱,跳,rap,篮球,此时我想向他学习这几门专业技能,奈何没有天赋,只能选择其一进行勤学苦练,只为有朝一日证明我是小~,等等不对差点露馅了,应该是坤流人物。回过头来,只选其一那就是enum的专长了。

首先创建一张enum_test的表结构,其中存放着一个kk的enum类型,其中包含着这位故人的所有技能。

请注意上图中enum是可以放空值的,回过头来,然后插入一些只包含一位的数据唱 , 跳, Rap, 篮球,进行勤学苦练,具体过程如下图。

除此之外,还可以用数字的形式进行表示,根据位置来标记选择的位置,比如选择唱就是1,跳就是2,以此类推。下图会将数据清空使用此方式再次插入一遍。

说明:从效率的角度出发,MySQL实现enum时采用选项标记每个字符串,方便进行查找,最多能够放65536个选项。

  • set

在enum的约束下只能选择一门,但是修炼了不一会,才发现自己原来是坤门的旷世奇才,于是将目光瞄向我这位故人的其余技能,势必要将坤门发扬光大。回过头来,选择至少一门,那就是set的专长了。

首先把原来表中的数据清空,将set类型添加到表结构enum_test中,然后将kk的enum类型从表结构中移除,具体过程如下图。

然后插入所有选择三种技能的数据的情况,具体过程如下图。

除此之外,也可以使用数字的形式插入,与enum略有不同可以选择多位,所以采用了二进制的方式,对应位置选或不选对应1/0,比如选择唱跳Rap,就对应着1110,注意二进制要逆转一下,即0111,转换成十进制为7,选择唱跳篮球,二进制对应着1011,转化为十进制为11,类比得跳Rap篮球为14,唱Rap篮球为13,下面清空表数据使用此方式再插入一遍。

可以看出此图与上面使用字符插入的对应,不过这样操作性极差,实际操作是不建议这样做的,一般还是按照字符串的形式插入即可。

补充:set有64位的二进制,用来表示情况,但是不要看这个位数小,能表示2^65^ 种情况。


如果我们想要知道选择唱 / 跳 / Rap / 篮球的有哪几种情况的话,这里就要介绍一下find_in_set(str,list),具体展示如下图。

为啥要介绍这一个接口呢,就是select根据一个条件是判断不出来的,只有所有条件都符合才会进行判断,具体看下图。

总而言之,枚举类型中enum只能从中选择一种,而set中可以选择多种,而且为了方便根据部分条件筛选出数据,还提供了find_in_set接口。

尾序

本篇主要介绍了MySQL的数据类型,用以创建更加丰富的表结构,主要从理论和实践的角度出发,对数据类型进行了基本的说明,并选出加黑部分进行了实践演示,理论并不难,主打就是一个手勤,干就完了。最后,我是舜华,期待与你的下一次相遇!

相关推荐
喵叔哟26 分钟前
重构代码之移动字段
java·数据库·重构
念白44329 分钟前
智能病历xml提取
数据库·sql·oracle
qingy_204633 分钟前
【JavaWeb】JavaWeb入门之XML详解
数据库·oracle
大数据面试宝典37 分钟前
用AI来写SQL:让ChatGPT成为你的数据库助手
数据库·人工智能·chatgpt
努力的小雨42 分钟前
快速上手 KSQL:轻松与数据库交互的利器
数据库·经验分享
Gentle5861 小时前
labview中连接sql server数据库查询语句
数据库·labview
Gentle5861 小时前
labview用sql server数据库存取数据到一个单元格
数据库·labview
2401_857636391 小时前
共享汽车管理新纪元:SpringBoot框架应用
数据库·spring boot·汽车
菲兹园长1 小时前
表的设计(MYSQL)
数据库·mysql
Java Fans1 小时前
MySQL数据库常用命令大全(完整版——表格形式)
数据库·mysql