07|第七话:基础篇-MySQL数据类型之字符串类型

前两话细聊了日期时间类型和数值类型,本篇文章来看下最后一个大的数据类型:字符串类型

字符串类型是实际工程中遇到最多的一种类型,MySQL提供了丰富的字符串类型,我们来总览看下:

类型 最大长度 存储空间要求 含义
CHAR(M) 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个字节 大型的字符串

其中M代表该数据类型最多能存储的字符数量,L代表我们实际向该类型的属性中存储的字符串在特定字符集下所占的字节数,W代表在该特定字符集下,编码一个字符最多需要的字节数。

目前还是有点懵,没关系,我们下面细细道来,本文的整体脉络如下:

07|第七话:基础篇-MySQL数据类型之字符串类型


07|第七话:基础篇-MySQL数据类型之字符串类型

一、CHAR(M)

CHAR(M)类型可以用来存储较短的字符串,有如下几个重要说明:
  • 【说明1】CHAR(M)中的M代表该类型最多可以存储的字符数量,注意,是字符数量,不是字节数量。其中M的取值范围是0~255。
  • 【说明2】填一个汉字算一个字符,填一个数字或字母也算是一个字符,而不用关心一个汉字需要多少个字节来存储。
  • 【说明3】如果省略掉M的值,那它的默认值就是1,也就是说CHAR和CHAR(1)是一个意思。
  • 【说明4】CHAR(0)是一种特别的类型,它只能存储空字符串”或者NULL值。
上面说到,一个汉字需要几个字节来存储,这实际上取决于数据库或该表使用的是哪种字符集。
CHAR(M)在不同的字符集下需要的存储空间也是不一样的,我们假设某个字符集编码一个字符最多需要W个字节,那么类型CHAR(M)占用的存储空间大小就是M×W个字节。比方说:
  • 对于采用ascii字符集的CHAR(5)类型来说,ascii字符集编码一个字符最多需要1个字节,也就是M=5、W=1,所以这种情况下该类型占用的存储空间大小就是5×1 = 5个字节。
  • 对于采用gbk字符集的CHAR(5)类型来说,gbk字符集编码一个字符最多需要2个字节,也就是M=5、W=2,所以这种情况下该类型占用的存储空间大小就是5×2 = 10个字节。
  • 对于采用utf8字符集的CHAR(5)类型来说,utf8字符集编码一个字符最多需要3个字节,也就是M=5、W=3,所以这种情况下该类型占用的存储空间大小就是5×3 = 15个字节。
当实际的字符不够填充M个时,怎么处理的呢?下面就是【说明5】:
  • 【说明5】如果保存时,数据的实际长度比CHAR类型声明的长度小,则会在右侧填充空格以达到指定的长度。这也就是说:一旦你确定了CHAR(M)类型的M的值,如果M的值很大,而你实际存储的字符串占用字节数又很少,会造成存储空间的浪费。
下面我们举例说明。
mysql> CREATE TABLE test_char1( c1 CHAR(0) , c2 CHAR, c3 CHAR(5) );
Query OK, 0 rows affected (0.03 sec)

mysql> desc test_char1
;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| c1    | char(0) | YES  |     | NULL    |       |
| c2    | char(1) | YES  |     | NULL    |       |
| c3    | char(5) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
3 rows in set (0.01 sec)

可以看到,CHAR和CHAR(1)是一个意思,验证了【说明3】的说法;我们对c1赋值测试,验证了【说明4】的说法:

mysql> insert into test_char1(c1) value('aaa');
ERROR 1406 (22001): Data too long for column 'c1' at row 1
mysql> insert into test_char1(c1) value('a');
ERROR 1406 (22001): Data too long for column 'c1' at row 1
mysql> insert into test_char1(c1) value('');
Query OK, 1 row affected (0.00 sec)

mysql> select * from test_char1
;
+------+------+------+
| c1   | c2   | c3   |
+------+------+------+
|      | NULL | NULL |
+------+------+------+
1 row in set (0.00 sec)

结合【说明1】,由于c2长度为1,那么就只能赋值一个字符,两个就会超出范围报错

mysql> insert into test_char1(c2) value('aa');
ERROR 1406 (22001): Data too long for column 'c2' at row 1
mysql> insert into test_char1(c2) value('你好');
ERROR 1406 (22001): Data too long for column 'c2' at row 1
mysql> insert into test_char1(c2) value('好');
Query OK, 1 row affected (0.00 sec)

mysql> select * from test_char1
;
+------+------+------+
| c1   | c2   | c3   |
+------+------+------+
| NULL | 好   | NULL |
+------+------+------+
2 rows in set (0.00 sec)

我们对c3字段赋值,由于它的长度是5,那么最多可以放5个字符:

mysql> update test_char1 set c3='你好哇哇';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select char_length(c3) from test_char1
;
+-----------------+
| char_length(c3) |
+-----------------+
|               4 |
+-----------------+
1 row in set (0.00 sec)

mysql> update test_char1 set c3
='你好哇哇哇哇';
ERROR 1406 (22001): Data too long for column 'c3' at row 1

07|第七话:基础篇-MySQL数据类型之字符串类型

二、VARCHAR(M)

一般情况下,字符串是长短不一的,如果使用CHAR会造成很大的空间浪费,VARCHAR(M)正是为了解决这个问题而生。

VARCHAR(M)中的M也是代表该类型最多可以存储的字符数量,最大允许多少呢?

mysql> CREATE TABLE test_varchar( NAME VARCHAR(65535)   );
ERROR 1074 (42000): Column length too big for column 'NAME' (max = 21845); use BLOB or TEXT instead
通过报错信息,可以看到M最大值是21845,再大就建议你别使用VARCHAR(M)了,直接使用BLOB或TEXT得了。
这里为啥是21845呢?原因是咱们创建的表默认的存储引擎是UTF8,一个字符最多占用三个字节,此外MySQL中还有一个规定,表中某一行包含的所有列中存储的数据大小总共不得超过65535个字节(注意是字节),所以字符大小理论上也就是不能大于65535/3=21845个了。
下面我们指定创建一个字符集为utf8mb4的表,看看最大的M值是不是发生了变化:
mysql> CREATE TABLE `test_varchar2` (   `NAME` varchar(65535) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ERROR 1074 (42000): Column length too big for column 'NAME' (max = 16383); use BLOB or TEXT instead
utf8mb4字符集中一个字符最多使用4个字节来编码,因此这里最大的字符数为65535/3=16383个,这边其实我们引出了一个MySQL中关于utf8和utf8mb4一个常见的考点:utf8字符集下,1字符使用1-3个字节表示,故而uft8也称之为utf8mb3;utf8mb4下,1字符使用1-4字节表示,比如一个表情是占用4个字节的,因此必须使用utf8mb4来承载,所以说,utf8mb4字符集才是正宗的utf8字符集!!!。
  • utf8mb3:“阉割”过的UTF-8字符集,只使用1-3字节表示字符。
  • utf8mb4:“正宗”的UTF-8字符集,使用1-4字节表示字符。
在MySQL8.0中,已经将utf8mb4设置为了默认字符集。
下一个重要问题,VARCHAR(M)类型占用的存储空间为什么是L+1 或 L+2 个字节?
一个VARCHAR(M)类型表示的数据其实是由这么两部分组成:
  • 真正的字符串内容:假设真正的字符串在特定字符集编码后占用的字节数为L。
  • 占用字节数(假设VARCHAR(M)类型采用的字符集编码一个字符最多需要W个字节,那么):
    • 当M×W < 256时,只需要一个字节来表示占用的字节数。
    • 当M×W >= 256且M×W < 65536时,需要两个字节来表示占用的字节数。
关于使用一个字节还是两个字节的问题,这个比较容易理解,一个字节占用8个比特位,能表示的最大无符号数就是255,两个字节占用16个比特位,能表示的最大无符号数就是65535。
下面我们来举例说明VARCHAR(M)类型的存储结构和大小计算。
字符集使用utf8,假设我们定义一个VARCHAR(5)类型,比如塞入“你好啊”这个字符串,那么实际字符串本身所占用的字节数为3*3=9字节,此外需要一个字节来表示长度,所以总共需要10字节,这里的9字节就是L,1就是额外记录字符长度的一个字节,也就是L+1。
假设我们定义一个VARCHAR(100)类型,依然塞入“你好啊”这个字符串,那么实际字符串本身所占用的字节数为3*3=9字节,由于100*3大于256,因此长度需要使用占用两个字节,因此总共占用11字节,也就是L+2。

07|第七话:基础篇-MySQL数据类型之字符串类型

所以我们说VARCHAR(M)是一种可变长度的字符串类型。

07|第七话:基础篇-MySQL数据类型之字符串类型

三、VARCHAR和CHAR类型

VARCHAR相对于CHAR类型,其实还有一个有趣的差异:MySQL检索CHAR类型的数据时,CHAR类型的字段会去除尾部的空格;检索VARCHAR类型的字段数据时,会保留数据尾部的空格。

举个简单的例子就可以很容易地理解这个区别了。

mysql> CREATE TABLE test_varchar3( c CHAR(4) , v VARCHAR(4) );
Query OK, 0 rows affected (0.03 sec)

mysql> desc test_varchar3
;
+-------+------------+------+-----+---------+-------+
| Field | Type       | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| c     | char(4)    | YES  |     | NULL    |       |
| v     | varchar(4) | YES  |     | NULL    |       |
+-------+------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

创建了一张表,定义了c char(4) 和 v varchar(4)两个字段,同时插入字符串“ab ”,注意ab后面有两个空格:

mysql> insert into test_varchar3 values('ab  ','ab  ');
Query OK, 1 row affected (0.00 sec)

mysql> select * from test_varchar3
;
+------+------+
| c    | v    |
+------+------+
| ab   | ab   |
+------+------+
1 row in set (0.00 sec)

mysql> select length(c),length(v) from test_varchar3
;
+-----------+-----------+
| length(c) | length(v) |
+-----------+-----------+
|         2 |         4 |
+-----------+-----------+
1 row in set (0.00 sec)

可以发现,c的长度只有2,而v的长度还是4,我们给每个字段后面追加一个“+”号可以看得比较明显:

mysql> select concat(c,'+'),concat(v,'+') from test_varchar3;
+---------------+---------------+
| concat(c,'+') | concat(v,'+') |
+---------------+---------------+
| ab+           | ab  +         |
+---------------+---------------+
1 row in set (0.01 sec)

concat()函数是mysql中内置的函数,用来做字符串拼接或多个字段的拼接输出,虽然没有系统介绍过,但是相信很容易可以理解这里的用法。

可以看到,char列最后的空格在做操作时都已经被删除,而varchar依然保留空格。
最后讨论一个实际问题:实际工程中我们该如何选择使用char和varchar?
通过以上内容的学习,我们已经知道char和varchar都是可以用来存储不算长的字符串,但是有几大点不同:
  • 1、char属于固定长度的字符类型,varchar属于可变长度的字符类型。
  • 2、他们的存储方式不太一样,varchar中还需要额外1个字节或2个字节的长度来保存后面实际字符串的占用的字节数,而char占用的字节数已经在初始定义的时候已经固定下来了。
  • 3、他们的检索方式也有点不同,检索时char会删除尾部的空格,varchar则会保留尾部的空格。
正是由于char是固定长度的,所以缺点太明显,就是在字符串长度不一的时候容易造成大量的存储空间浪费,所以char比较适合用于长度变化不大的数据,比如存储身份证这种固定长度的字符串会比较适合。
但是随着MySQL的版本升级,varchar类型的性能也在不断提高,因此varchar被应用的更加广泛。

07|第七话:基础篇-MySQL数据类型之字符串类型

四、各种TEXT类型

虽然VARCHAR(M)已经可以存储很长的字符串了,可是有时候还是不够咋办?不用担心,MySQL给我们提供了TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT四种可以存储大型的字符串的类型,它们也都是变长类型,也就是说这些类型占用的存储空间由实际内容和内容占用的字节长度两部分构成。
  • 因为TINYTEXT最多可以存储2⁸-1个字节,所以内容占用的字节长度用1个字节就可以表示。
  • TEXT最多可以存储2¹⁶-1个字节,所以内容占用的字节长度用2个字节就可以表示。
  • MEDIUMTEXT最多可以存储2²⁴-1个字节,所以内容占用的字节长度用3个字节就可以表示。
  • LONGTEXT最多可以存储2³²-1个字节,所以内容占用的字节长度用4个字节就可以表示。
一个表中如果有的属性需要存储特别长的文本的话,就可以考虑使用这几个类型了。
在向TEXT类型的字段保存和查询数据时,系统自动按照实际长度存储,不需要预先定义长度。
关于varchar类型和text类型如何选择,一般遵循这个原则:TEXT文本类型,可以存比较大的文本段,搜索速度稍慢,因此如果不是特别大的内容,建议使用CHAR,VARCHAR来代替。

varchar类型和text类型的比较是比较有说头的,牵涉到更加具体的存储模式,我们日后在学习底层原理的时候我们再来对这两者做个深入比较。

MySQL其实还有一些比较小众的类型,比如ENUM类型、SET类型以及各种二进制类型,不过这些在实际生产系统中很少见到,因此在这里不展开赘述,如果哪一天你的系统中用到了这些类型,也请考虑下为什么使用,是不是换成其他类型或存储方式更加合适。

07|第七话:基础篇-MySQL数据类型之字符串类型

五、总结

字符串类型是我们平时实际工程中使用最频繁的一种类型,我们必须要了解其特性,在设计表结构时才能游刃有余,本文也是初探,后续我们还会更加深入地探讨,目前了解这么多就够了。本文需要搞清楚的重点问题是:
  • CHAR(M)和VARCHAR(M)两种类型分别有什么特点?有什么区别?
  • 字符串类型在不同的字符集下占用的空间是如何计算的?
  • TEXT类型有哪些?在什么场景下使用,跟varchar类型的区别感兴趣可以提前去了解下。

原文始发于微信公众号(幕后哈土奇):07|第七话:基础篇-MySQL数据类型之字符串类型

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/112887.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!