⼀⽂搞懂MySQL的数据类型中长度的含义
我们在数据库建表时,经常会困扰某个字段应该选择什么数据类型,以及填写什么长度。选择数据类型⽅⾯⼀般不会有什么⼤问题,但是在填写对应的长度的时候,很多⼈就会困扰,对应长度填写的数字到底是什么含义,以及会影响到哪些东西。笔者在翻阅⽹上的相关⽂章时,发现⼀⼤半⽂章写的都是错的,主要的问题在于搞混了“字符”和“字节”这两者的含义,甚⾄有的⼈觉得这就是⼀回事。如果对字符和字节不理解的读者,可以先阅读《⼀⽂搞懂字符和字节的含义》。本⽂我们通过实例来介绍MySQL的数据类型中长度的含义,读完本⽂能够让你在数据库建表的时候不再困惑。
字符串类型
常⽤的字符串类型的数据类型有 CHAR 和 VARCHAR 两种,两者后⾯都需要跟上⼀个数字表⽰长度,例如
CHAR(10)
VARCHAR(10)
CHAR(n) 和 VARCHAR(n) 两者中的 n 含义均为该字段最⼤可容纳的字符数。(注意早期的版本中,n指的是字节数,你也不需要关注是哪些版本,因为是⼗多年前的版本了,估计⼀般⼈也⽤不到)。
占⽤空间
CHAR(n) 和 VARCHAR(n) 字段值的占⽤空间不是固定的,⽽是由实际存⼊的内容决定的,但在细节上两者有⼀些不同。我们均以 n=4 为例。
对于 CHAR(4) 表⽰固定容纳4个字符,当少于4个字符时,会使⽤空格填充空缺的部分,使其达到4个字符。如果超过4个字符,会⾃动截断超出部分。例如你存⼊数据为 'ab' ,实际会存⼊ 'ab ' (ab后有2个空格),因此占⽤4个字节。以下以⼏个案例作为演⽰:
'a啊b' —— 字符数为3,少1个⽤空格补齐,因此实际存⼊ 'a啊b ' ,字符数:4,字节数:1+3+1+1=6
'a啊b哈ccccccccc' —— 字符数超出4,仅保留前4个字符,因此实际存⼊ 'a啊b哈' ,字符数:4,字节数:1+3+1+3=8
'a啊和哈' —— 字符数刚好为4,不需要截断和补齐,因此实际存⼊ 'a啊和哈' ,字符数:4,字节数:1+3+3+3=10
对于 CHAR 字段,你在使⽤ CHAR_LENGTH() 和 LENGTH() 函数查询时,会发现和以上描述的情况不⼀致,我们放上代码演⽰:
(备注: CHAR_LENGTH() 函数返回字符串的字符数, LENGTH() 函数返回字符串的字节数)
-- 假定已存在表 tb ,其中包含字段 s_char 的数据类型定义为 CHAR(4) ,我们先进⾏插⼊操作,获取插⼊⾏id=1
INSERT INTO `tb`(`s_char`) VALUES ('啊a');
-- 接下去查询该⾏ SELECT s_char, CHAR_LENGTH(s_char), LENGTH(s_char) FROM `tb` WHERE id=1;
-- 结果为:s_char=>'啊a',CHAR_LENGTH(s_char)=>2,LENGTH(s_char)=>4
你会发现以上结果跟预想中的不⼀致,按照⼀般理解预期存⼊ '啊a' ,仅为2个字符,需补充2个空格,实际存⼊为 '啊a ' ,因此字符数为4,字节数为 3+1+1+1=6 。
这⾥造成偏差的原因并不是错误,⽽是 CHAR 字段在检索输出时,⾃动省略了右侧的空格。我们来演⽰⼀遍完整的流程:
预期存⼊ '啊a' ,少于4个字符,补充2个空格,因此实际存⼊的值为 '啊a ' ,该值字符数为4,字节数为6。在检索时,原值为 '啊a ' ,输出时⾃动省略右侧空格,实际输出为 '啊a' ,该字符串字符数为2,字节数为4。
下⾯再来说说 VARCHAR 类型,依然以 n=4 为例。区别于 CHAR 类型的补空, VARCHAR 类型对于未达到 n 字符的情况不会补空。
关于计算 VARCHAR 类型字符串的占⽤空间,有⼀点需要说明的是, VARCHAR 类型字符串的占⽤空间实际上包含2部分,⼀是存储数据本⾝占⽤的空间,⼆是描述数据的元数据占⽤的空间,例如 VARCHAR 类型会使⽤1个字节记录存⼊数据实际的字符数。下述描述的“占⽤空间”特指前者,即存储数据本⾝占⽤的空间,不包含描述数据的元数据占⽤的空间。(其他数据类型等同)
以下以⼏个案例作为演⽰:
(1) 'a啊b' —— 字符数为3,不补空,实际存⼊为 'a啊b' ,字符数为3,字节数为 1+3+1=5 。
(2)'a啊b哈ccccccccc' —— 字符数超出4,仅保留前4个字符,因此实际存⼊ 'a啊b哈' ,字符数:4,字节数:1+3+1+3=8 。这种情况和 CHAR 类型处理⼀致。
(3)'a啊和哈' —— 字符数刚好为4,不需要截断和补齐,因此实际存⼊ 'a啊和哈' ,字符数:4,字节数:1+3+3+3=10
整数类型
常⽤的整数数据类型有 tinyint ,smallint ,mediumint , int ,bigint 共计5种,在声明列时,后⾯也可以跟上 n ,例如 int(n) 。实际上这⾥的 n ⾮常鸡肋,⼏乎没有任何使⽤场景。它的含义是“显⽰位宽”,这个 n ⽆论填任何数,不影响存储环节,仅影响在检索时的输出格式,⽽且在⾮常严格的情况下才成⽴。我们描述⼀种应⽤场景:我们声明某列(列名取int_5)为 int(5) ,在声明列的时候,要使⽤到该特性,必须加上 zerofill (填充0)属性,即语句为
`int_5` int(5) unsigned zerofill DEFAULT NULL
-- 备注:加zerofill必须同时加unsigned
当插⼊的数字⼩于5位时,在特定客户端检索输出时,会在数字前“补0”,凑⾜5位数字。(⼤于5位则原数字原样显⽰)例如存储的数字是123,那么输出00123 。说它鸡肋,主要有以下⼏个原因:
(1)对存储环节没有任何帮助,仅改变输出显⽰环节。⽽“格式化显⽰”⼀般在前端或者后端的应⽤层操作就可以了,⽆需在数据库中输出时操作。
(2)格式化⽅式仅仅只有“补0”⼀种⽅式。
(3)仅针对特定客户端输出时才有显⽰效果,⽬前仅发现使⽤MySQL Shell才有显⽰效果,其他客户端连接时均⽆。
由于以上原因,所以⼏乎没有开发者会使⽤这个特性。
占⽤空间这5种整型的占⽤空间是固定的,均与其后设置的 n ⽆关,例如设置字段类型为 int ,则⽆论 n 设置什么,它占⽤的空间就是4个字节。这5种整型的占⽤空间分别是: tinyint :1个字节,smallint :2个字节,mediumint :3个字节,int :4个字节,bigint :8个字节。字符串长度的正确表示
很多⼈说经常记不住他们的取值范围,实际上很好算,例如 tinyint 占⽤1个字节,也就是8位,每1位都包含0和1两种情况,因此共2的8次⽅为256种情况,如果是⽆符号(unsigned),取值范围就是0⾄255。如果是有符号情况,由于第1位要⽤来表⽰符号,因此可⽤7位表⽰数字,2的7次⽅为128,再加上符号,取值范围为 -128⾄127 。其它⼏种数据类型也可以按照这个⽅法计算。
怕有的⼈还是难以理解,这⾥再重复⼀遍,以 int 为例,⽆论 int(n) 中的 n 设置什么值,⽆论插⼊的这个值或⼤或⼩,只要在取值范围内,那这个字段就是占⽤4个字节。
另外再补充⼀点,当插⼊的值,超出取值范围的时候,MySQL并不会报错,⽽是⾃动变成成在取值范围内最接近该值的边界值。例如字段为 tinyint ,有符号型时取值范围 -128⾄127,当你输⼊-222时,不会报错,会⾃动存⼊最接近-222的-128,当你输⼊222时,会⾃动存⼊127。这⼀点需要尤其注意,否则很容易造成巨⼤的bug。
浮点型
FLOAT 类型固定占⽤4个字节, DOUBLE 类型固定占⽤8个字节,逻辑和上述的整型类似,不再赘述。
下⾯我们来说说 DECIMAL 类型,它的定义⽅式是 DECIMAL(M,D) ,其中 M 表⽰最⼤位数,D 表⽰⼩数点右侧的位数。这⾥的“位”不是⼆进制的⽐特位,⽽是指⼗进制的数字的位数。
例如我们定义 DECIMAL(5,2) ,则表⽰最⼤位数为5位,⼩数点后2位,因此⼩数点前还剩下3位,于是取值范围为 -999.99⾄999.99 。可以这样理解:M-D 的值为⼩数点前的位数,D 的值为⼩数点后的位数,要算取值范围则各个位置填充9,取正负范围。那么容易计算 DECIMAL(5,1) 的取值范围是 -9999.9⾄9999.9 ; DECIMAL(4,2) 的取值范围是 -99.99⾄99.99。
占⽤空间
DECIMAL(M,D) 的存储⽅式和其他数字类型都完全不同,它是以字符串形式进⾏存储的。这可能有点不好理解,以整型 tinyint 为例,它存储的值是直接为⼗进制到⼆进制的转换,以⽆符号型为例,当需要存⼊的值为100值,将100转化为⼆进制为1100100 ,使⽤1个字节即8位记录,实际存⼊的是 01100100 。但是⽤ DECIMAL 类型存储时,⽐如定义DECIMAL(3,0) ,存⼊100时,实际存⼊的是由字符“1”,“0”,“0”拼接⽽成的字符串“100”的⼆进制值,存⼊时占⽤3个字节,分别是31,30,30(注意这是⼗六进制)。
1个数字字符占⽤1个字节,因此定义为 DECIMAL(M,D) 占⽤ M 个字节。
(同上所述,M个字节为数据本⾝的占⽤空间,另外描述该数据的元数据还固定占⽤2个字节的空间)。需要注意的是, DECIMAL 类型在存储时有补0操作。⼩数点前不⾜,向更⾼位补0,⼩数点后不⾜,向更低位补0。以 DECIMAL(5,2) 为例,如果准备存⼊9.5,⼩数点前应为3位,缺2位,⼩数点后应为2位,缺1位,各补0后,实际存⼊ '009.50' ,转化为⼗六进制为30 30 39 2E 35 30 。但是在检索输出时,⼩数点前的0⼀般会省略,⽽⼩数点后的0会保留,这⼀点也需要注意。以上就是最长使⽤的3种数据类型的长度含义以及其占⽤空间,理解了以上概念,在使⽤MySQL时,将会更得⼼应⼿。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。