MySQL CHAR和VARCHAR存储、读取时的差别

导读

你真的知道CHAR和VARCHAR类型在存储和读取时的区别吗?

还是先抛几条结论吧:

1、存储的时候,CHAR总是会补足空格后再存储,不管用户插入数据时尾部有没有包含空格。

2、存储的时候,VARCHAR不会先补足空格后再存储,但如果是用户在插入时特地加了空格那就会如实存储,而不会给删除。

3、读取数据时,CHAR总是会删除尾部空格(哪怕是写入时包含空格)。

4、读取数据时,VARCHAR总是如实取出之前存入的值(如果存储时尾部包含空格,就会继续保留着,不会像CHAR那样删除尾部空格)。

下面是测试验证过程。

1、测试CHAR类型

表结构:

CREATE TABLE `tchar` (
 `id` int(10) unsigned NOT NULL DEFAULT '0',
 `c1` char(20) NOT NULL DEFAULT '',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

插入几条记录:

insert into tchar values (1, concat('a', repeat(' ',19)));
insert into tchar values (2, concat(' ', repeat('a',19)));
insert into tchar values (3, 'a');
insert into tchar values (4, ' ');
insert into tchar values (5, '');

查看存储结构:

(1) INFIMUM record offset:99 heapno:0 ...
(2) SUPREMUM record offset:112 heapno:1 ...
(3) normal record offset:126 heapno:2 ... <- id=1
(4) normal record offset:169 heapno:3 ... <- id=2
(5) normal record offset:212 heapno:4 ... <- id=3
(6) normal record offset:255 heapno:5 ... <- id=4
(7) normal record offset:298 heapno:6 ... <- id=5

看到这坨东西有点懵是不是,还记得我给你们安利过的一个工具不,看这里:innblock | InnoDB page观察利器。

可以看到,无论我们存储多长的字符串进去,每条记录实际都是占用43(169-126=43)字节。由此结论1成立。
简单说下,43字节的由来:
DB_TRX_ID, 6字节。
DB_ROLL_PTR, 7字节。
id, int, 4字节。
c1, char(20), 20字节;因为是CHAR类型,还需要额外1字节。
每条记录总是需要额外5字节头信息(row header)。
这样总的加起来就是43字节了。

再看下读取tchar表的结果:

select id,concat('000',c1,'$$$'),length(c1) from tchar ;
+----+----------------------------+------------+
| id | concat('000',c1,'$$$')  | length(c1) |
+----+----------------------------+------------+
| 1 | 000a$$$     |   1 | <- 删除尾部空格
| 2 | 000 aaaaaaaaaaaaaaaaaaa$$$ |   20 |
| 3 | 000a$$$     |   1 |
| 4 | 000$$$      |   0 | <- 删除尾部空格,结果和id=5一样
| 5 | 000$$$      |   0 |
+----+----------------------------+------------+

2、测试VARCHAR类型

表结构:

CREATE TABLE `tvarchar` (
 `id` int(10) unsigned NOT NULL DEFAULT '0',
 `c1` varchar(20) NOT NULL DEFAULT '',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

插入几条记录:

insert into tvarchar values (1, concat('a', repeat(' ',19)));
insert into tvarchar values (2, concat(' ', repeat('a',19)));
insert into tvarchar values (3, 'a');
insert into tvarchar values (4, ' ');
insert into tvarchar values (5, '');
insert into tvarchar values (6, '');

查看存储结构:

(1) INFIMUM record offset:99 heapno:0 ...
(2) SUPREMUM record offset:112 heapno:1 ...
(3) normal record offset:126 heapno:2 ... <- id=1
(4) normal record offset:169 heapno:3 ... <- id=2
(5) normal record offset:212 heapno:4 ... <- id=3
(6) normal record offset:236 heapno:5 ... <- id=4
(7) normal record offset:260 heapno:6 ... <- id=5
(8) normal record offset:283 heapno:7 ... <- id=6

可以看到,几条记录的字节数分别是:43、43、24、24、23、23(最后一条记录和id=5那条记录一样)。
对上面这个结果有点诧异是不是,尤其是id=1的记录(插入的是'a…后面19个空格'),居然也要消耗43字节,这就佐证了上面的结论2。
同样的,id=3和id=4这两条记录都是占用24字节,而id=5和id=6这两条记录都是占用23字节(没有额外存储字符串的字节数,只有id列4个字节)。

再看下读取tvarchar表的结果:

select id,concat('000',c1,'$$$'),length(c1) from tvarchar;
+----+----------------------------+------------+
| id | concat('000',c1,'$$$')  | length(c1) |
+----+----------------------------+------------+
| 1 | 000a     $$$ |   20 | <- 读取结果中没有删除尾部的空格
| 2 | 000 aaaaaaaaaaaaaaaaaaa$$$ |   20 |
| 3 | 000a$$$     |   1 |
| 4 | 000 $$$     |   1 | <- 读取结果中没有删除此空格
| 5 | 000$$$      |   0 |
| 6 | 000$$$      |   0 |
+----+----------------------------+------------+

总的来说,可以总结成两条结论:
1、从读取的结果来看,CHAR类型列看起来像是在存储时把空格给吃了,但实际上只是在读取时才给吃了(显示层面上把空格删除了)。
2、从读取的结果来看,VARCHAR类型列看起来像是反倒保留了多余的空格,实际上也是只在读取时才恢复这些空格(但实际物理存储时还是会删掉这些空格)。

最后,来看下文档里怎么说的:

When CHAR values are stored, they are right-padded with spaces to the
specified length. 简言之,CHAR列在存储时尾部加空格补齐长度。

When CHAR values are retrieved, trailing spaces are removed unless the
PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.
简言之,CHAR列在读取时会去掉尾部空格,除非设置sql_mode值PAD_CHAR_TO_FULL_LENGTH=1。

VARCHAR values are not padded when they are stored.
简言之,存VARCHAR时尾部不加空格。

Trailing spaces are retained when values are stored and retrieved, in
conformance with standard SQL. 简言之,读取VARCHAR时会显示空格。

以上测试使用的版本及环境:

mysql> select version()\G
...
version(): 8.0.15

mysql> select @@sql_mode\G
...
@@sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

参考文档

11.4.1 The CHAR and VARCHAR Types,https://dev.mysql.com/doc/refman/5.7/en/char.html

以上就是MySQL CHAR和VARCHAR存储的差别的详细内容,更多关于MySQL CHAR和VARCHAR的资料请关注我们其它相关文章!

(0)

相关推荐

  • Mysql中varchar长度设置方法

    如果某一项中设置的是varchar(50) 那么对英文当然是50 那么对中文呢 utf-8的中文占3个字节 那么,这个varchar(50)是不是只能存16个汉字了? mysql varchar(50) 不管中文 还是英文 都是存50个的 MySQL5的文档,其中对varchar字段类型这样描述:varchar(m) 变长字符串.M 表示最大列长度.M的范围是0到65,535.(VARCHAR的最大实际长度由最长的行的大小和使用的字符集确定,最大有效长度是65,532字节). 为何会这般变换?真

  • MySQL中VARCHAR与CHAR格式数据的区别

    区别 CHAR与VARCHAR类型类似,但它们保存和检索的方式不同.CHAR有固定的长度,而VARCHAR属于可变长的字符类型.它们最大长度和是否尾部空格被保留等方面也不同.在存储和检索过程中不进行大小写转换. 下面的表格显示了将各种字符串值保存到CHAR(4)和VARCHAR(4)列后的结果,说明了CHAR和VARCHAR之间的差别: 值 CHAR(4) 存储需求 VARCHAR(4) 存储需求 '' ' ' 4个字节 '' 1个字节 'ab' 'ab ' 4个字节 'ab' 3个字节 'ab

  • Mysql数据库中把varchar类型转化为int类型的方法

    在上篇文章给大家讲了MySQL数据库中把int转化varchar引发的慢查询,本文给大家介绍Mysql数据库中把varchar类型转化为int类型的方法,一起看看吧! mysql为我们提供了两个类型转换函数:CAST和CONVERT,现成的东西我们怎能放过? CAST() 和CONVERT() 函数可用来获取一个类型的值,并产生另一个类型的值. 这个类型 可以是以下值其中的 一个: BINARY[(N)] CHAR[(N)] DATE DATETIME DECIMAL SIGNED [INTEG

  • 深入研究mysql中的varchar和limit(容易被忽略的知识)

    为什么标题要起这个名字呢?commen sence指的是那些大家都应该知道的事情,但往往大家又会会略这些东西,或者对这些东西一知半解,今天我总结下自己在mysql中遇到的一些commen sense类型的问题. 1.varchar(5)可以存储多少个汉字,多少个字母数字? 相信有好多人应该跟我一样,对这个已经很熟悉了,根据经验我们能很快的做出决定,比如说用varchar(200)去存储url等等,但是,即使你用了很多次也很熟悉了,也有可能对上面的问题做出错误的回答. 这个问题我查了好多资料,有的

  • 探究MySQL中varchar的定义长度

    UTF8字符集下: SQL>create table test(id int auto_increment,name varchar(10),primary key(id)); SQL>insert into test values(null,'1234567890'); Query OK, 1 row affected (0.00 sec) SQL>insert into test values(null,'一二三四五六七八九十'); Query OK, 1 row affected

  • MYSQL中 char 和 varchar的区别

    CHAR和VARCHAR类型相似,差别主要在存储,尾随空格和检索方式上. CHAR和VARCHAR相同的是:CHAR和VARCHAR都指定了字符长度,注意是字符长度.例如char(30) 和 varchar(30)表示都可以存30个字符.有一点要注意的是在utf8mb4编码中,每个字符占4个节点.在utf8中,每个字符占3个字节.当要存储的字符超过CHAR/VARCHAR指定的最大长度.在sql mode 没开启的时候是截断要存储的字串,只存储前30位 CHAR列中的值是定长的字符串.长度可以指

  • mysql varchar类型求和实例操作

    有的小伙伴在学习数据库的时候,创建表结构的时候不小心把某字段设置成了varchar但是在统计求和的时候就傻眼了,接下来跟着小编学习一下,不用改该列数据类型也能求和的方法吧! 1.打开 数据库连接客户端Navicat Premium ,创建一个新的表结构,这里age这列 故意 设置为 varchar. 2.创建表成功之后,为刚刚的表创建一些测试的数据,这里如下图: 3.在数据量少的时候可以使用sum()函数直接求和,因为MySQL中它可以自动识别是字符串类型还是数字类型. 4.以上适用于整数,或者

  • MySQL动态修改varchar长度的方法

    虽然这种情况不应该发生,通常像我们关系型数据库,我们应该是事先设计好,以后不能改动,但是由于之前工作的疏忽,其实说实话,也不仅仅是我个人的疏忽,主要是沟通上的原因,当然数据库毕竟是我设计的,所以,还是自我批评一下. 说一下情况:MySQL字段有个varchar值字段设置的太短了,设置了30个,(我依稀记得varchar是可扩展的,当然现实并不容忍我的依稀),所以我只能找一个方法在保证数据库数据不变的情况下,动态修改varchar字段的长度,找了一段时间,终于让我找到了. alter table

  • MySQL中把varchar类型转为date类型方法详解

    如下表: 先使用str_to_date函数,将其varchar类型转为日期类型,然后从小到大排序 语法:select str_to_date(class_time,'%Y%m%d %H:%i:%s') a from a order by a desc ; 下面接着看下oracle中varchar类型的日期格式转换date类型 oracle中varchar类型的日期格式转换date类型 SELECT to_char(to_date(m.ma_datetime,'yyyy-MM-dd hh24:mi

  • MySQL数据库中把int转化varchar引发的慢查询

    最近一周接连处理了2个由于int向varchar转换无法使用索引,从而引发的慢查询. CREATE TABLE `appstat_day_prototype_201305` ( `day_key` date NOT NULL DEFAULT '1900-01-01', `appkey` varchar(20) NOT NULL DEFAULT '', `user_total` bigint(20) NOT NULL DEFAULT '0', `user_activity` bigint(20)

  • MySQL中字段类型char、varchar和text的区别

    在MySQL中,char.varchar和text类型的字段都可以用来存储字符类型的数据,char.varchar都可以指定最大的字符长度,但text不可以. 它们的存储方式和数据的检索方式也都不一样. 数据的检索效率是:char > varchar > text 具体说明: char:存储定长数据很方便,CHAR字段上的索引效率级高,必须在括号里定义长度,可以有默认值,比如定义char(10),那么不论你存储的数据是否达到了10个字节,都要占去10个字节的空间(自动用空格填充),且在检索的时

  • 对比MySQL中int、char以及varchar的性能

    网络上有许多似是而非的"谣言",当然都不是恶意,绝大部分都是开发者不愿意自己主动研究,反而轻信其他人的信口之言. 关于数据库的谣言也有不少,比如"int性能比char高很多". 我最近针对int.long.char.varchar进行了一次性能测试,发现它们其实并没有太大的性能差距: 备注:c8=char(8), s8=varchar(8), i8=(bigint), c4=char(4), s4=varchar(4), i4=char(4) 100w行无索引情况下

随机推荐