MySQL存储秘密揭示:CHAR vs.VARCHAR,解锁定长神器的终极选择指南!

2023-11-20 15:56:48 浏览数 (2)

CHAR和VARCHAR类型在存储和检索方式上有相似之处,但在最大长度以及是否保留尾随空格方面存在差异。

1. 存储方面

1.1 存储对比

CHAR和VARCHAR类型的声明包含一个长度,表示要存储的最大字符数。例如,CHAR(30)可以容纳最多30个字符。

CHAR列的长度在创建表时被固定为您声明的长度。长度可以是从0到255的任何值。当存储CHAR值时,它们会被右填充到指定的长度。当检索CHAR值时,除非启用了PAD_CHAR_TO_FULL_LENGTH SQL模式(默认没启用),否则会删除尾随空格。

VARCHAR列中的值是可变长度字符串。长度可以指定为0到65,535的值。VARCHAR的有效最大长度取决于最大行大小(65,535字节,这个大小被所有列共享)和所使用的字符集。

1.2 具体对比

假设当前一个表里包含类型为char(4)及varchar(4)的列(假设该列使用单字节字符集如latin1),则,存储所需的大小对比如下:

注:

  • 如果字符集为其他类型(utf8mb4),则根据存储的内容进行换算即可
  • 上例中为了演示,非严格模式时可以截断存储

2. 显示及检索对比

本文后续案例是在utf8的字符集的实例上进行演示的

2.1 默认SQL模式

默认没开启PAD_CHAR_TO_FULL_LENGTH SQL模式的情况下,char及varchar的显示几乎没有区别,例如:

代码语言:javascript复制
mysql> create table tb_char (id int primary key auto_increment, c_char char(10),c_varchar  varchar(10));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into tb_char (c_char,c_varchar) values ('a','a'),('张三','张三'),('tc一','tc一');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

查询显示如下:

代码语言:javascript复制
mysql> select * from  tb_char;
 ---- -------- ----------- 
| id | c_char | c_varchar |
 ---- -------- ----------- 
|  1 | a      | a         |
|  2 | 张三   | 张三      |
|  3 | tc一   | tc一      |
 ---- -------- ----------- 
3 rows in set (0.00 sec)

查看长度如下:

代码语言:javascript复制
mysql> select c_char,c_varchar, length(c_char)len_char,length(c_varchar)len_varchar,char_length(c_char),char_length(c_varchar) from tb_char;
 -------- ----------- ---------- ------------- --------------------- ------------------------ 
| c_char | c_varchar | len_char | len_varchar | char_length(c_char) | char_length(c_varchar) |
 -------- ----------- ---------- ------------- --------------------- ------------------------ 
| a      | a         |        1 |           1 |                   1 |                      1 |
| 张三   | 张三      |        6 |           6 |                   2 |                      2 |
| tc一   | tc一      |        5 |           5 |                   3 |                      3 |
 -------- ----------- ---------- ------------- --------------------- ------------------------ 
3 rows in set (0.00 sec)

从显示结果来看,char与varchar在此时看上去是一致的,即:查询时,char类型的数据会将末尾空格去除。

此时,如果写入末尾带空格的值,则结果会如何呢?

代码语言:javascript复制
mysql> insert into tb_char (c_char,c_varchar) values ('abc ','abc ');
Query OK, 1 row affected (0.01 sec)

mysql> select * from  tb_char;
 ---- -------- ----------- 
| id | c_char | c_varchar |
 ---- -------- ----------- 
|  1 | a      | a         |
|  2 | 张三   | 张三      |
|  3 | tc一   | tc一      |
|  4 | abc    | abc       |
 ---- -------- ----------- 
4 rows in set (0.00 sec)

mysql> select c_char,c_varchar, length(c_char)len_char,length(c_varchar)len_varchar,char_length(c_char),char_length(c_varchar) from tb_char;
 -------- ----------- ---------- ------------- --------------------- ------------------------ 
| c_char | c_varchar | len_char | len_varchar | char_length(c_char) | char_length(c_varchar) |
 -------- ----------- ---------- ------------- --------------------- ------------------------ 
| a      | a         |        1 |           1 |                   1 |                      1 |
| 张三   | 张三      |        6 |           6 |                   2 |                      2 |
| tc一   | tc一      |        5 |           5 |                   3 |                      3 |
| abc    | abc       |        3 |           4 |                   3 |                      4 |
 -------- ----------- ---------- ------------- --------------------- ------------------------ 
4 rows in set (0.00 sec)

此时,结果出现了不同,char类型的显示依旧将末尾空格去除,而varchar则原样显示

如果此时按条件查询‘abc’及‘abc ’,结果会如何?

代码语言:javascript复制
mysql> select * from  tb_char where c_char ='abc';
 ---- -------- ----------- 
| id | c_char | c_varchar |
 ---- -------- ----------- 
|  4 | abc    | abc       |
 ---- -------- ----------- 
1 row in set (0.00 sec)

mysql> select * from  tb_char where c_varchar ='abc';
 ---- -------- ----------- 
| id | c_char | c_varchar |
 ---- -------- ----------- 
|  4 | abc    | abc       |
 ---- -------- ----------- 
1 row in set (0.00 sec)

mysql> select * from  tb_char where c_char ='abc ';
 ---- -------- ----------- 
| id | c_char | c_varchar |
 ---- -------- ----------- 
|  4 | abc    | abc       |
 ---- -------- ----------- 
1 row in set (0.00 sec)

mysql> select * from  tb_char where c_varchar ='abc ';
 ---- -------- ----------- 
| id | c_char | c_varchar |
 ---- -------- ----------- 
|  4 | abc    | abc       |
 ---- -------- ----------- 
1 row in set (0.00 sec)

结果是都可以显示。

不过,如果做了concat操作,结果如下:

代码语言:javascript复制
mysql> select c_char,c_varchar,concat(c_char,'concat'),concat(c_varchar,'concat') from tb_char;
 -------- ----------- ------------------------- ---------------------------- 
| c_char | c_varchar | concat(c_char,'concat') | concat(c_varchar,'concat') |
 -------- ----------- ------------------------- ---------------------------- 
| a      | a         | aconcat                 | aconcat                    |
| 张三   | 张三      | 张三concat              | 张三concat                 |
| tc一   | tc一      | tc一concat              | tc一concat                 |
| abc    | abc       | abcconcat               | abc concat                 |
 -------- ----------- ------------------------- ---------------------------- 

2.2 PAD_CHAR_TO_FULL_LENGTH 模式

将SQL模式改为PAD_CHAR_TO_FULL_LENGTH后,再对比看一下。

代码语言:javascript复制
mysql>  set sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';
Query OK, 0 rows affected (0.00 sec)

mysql> select * from  tb_char;
 ---- ---------------- ----------- 
| id | c_char         | c_varchar |
 ---- ---------------- ----------- 
|  1 | a              | a         |
|  2 | 张三           | 张三      |
|  3 | tc一           | tc一      |
|  4 | abc            | abc       |
 ---- ---------------- ----------- 
4 rows in set (0.00 sec)

mysql> select c_char,c_varchar, length(c_char)len_char,length(c_varchar)len_varchar,char_length(c_char),char_length(c_varchar) from tb_char;
 ---------------- ----------- ---------- ------------- --------------------- ------------------------ 
| c_char         | c_varchar | len_char | len_varchar | char_length(c_char) | char_length(c_varchar) |
 ---------------- ----------- ---------- ------------- --------------------- ------------------------ 
| a              | a         |       10 |           1 |                  10 |                      1 |
| 张三           | 张三      |       14 |           6 |                  10 |                      2 |
| tc一           | tc一      |       12 |           5 |                  10 |                      3 |
| abc            | abc       |       10 |           4 |                  10 |                      4 |
 ---------------- ----------- ---------- ------------- --------------------- ------------------------ 
4 rows in set (0.00 sec)

此时使用concat,结果如下:

代码语言:javascript复制
mysql> select c_char,c_varchar,concat(c_char,'concat'),concat(c_varchar,'concat') from tb_char;
 ---------------- ----------- ------------------------- ---------------------------- 
| c_char         | c_varchar | concat(c_char,'concat') | concat(c_varchar,'concat') |
 ---------------- ----------- ------------------------- ---------------------------- 
| a              | a         | a         concat        | aconcat                    |
| 张三           | 张三      | 张三        concat      | 张三concat                 |
| tc一           | tc一      | tc一       concat       | tc一concat                 |
| abc            | abc       | abc       concat        | abc concat                 |
 ---------------- ----------- ------------------------- ---------------------------- 
4 rows in set (0.00 sec)

可见,此时char类型每一行的末尾都已经填充了空格。

使用‘abc’及‘abc ’查看结果:

代码语言:javascript复制
mysql> select * from  tb_char where c_char ='abc';
 ---- ------------ ----------- 
| id | c_char     | c_varchar |
 ---- ------------ ----------- 
|  4 | abc        | abc       |
 ---- ------------ ----------- 
1 row in set (0.00 sec)

mysql> select * from  tb_char where c_varchar ='abc';
 ---- ------------ ----------- 
| id | c_char     | c_varchar |
 ---- ------------ ----------- 
|  4 | abc        | abc       |
 ---- ------------ ----------- 
1 row in set (0.00 sec)

mysql> select * from  tb_char where c_char ='abc ';
 ---- ------------ ----------- 
| id | c_char     | c_varchar |
 ---- ------------ ----------- 
|  4 | abc        | abc       |
 ---- ------------ ----------- 
1 row in set (0.00 sec)

mysql> select * from  tb_char where c_varchar ='abc ';
 ---- ------------ ----------- 
| id | c_char     | c_varchar |
 ---- ------------ ----------- 
|  4 | abc        | abc       |
 ---- ------------ ----------- 
1 row in set (0.00 sec)

mysql> select * from  tb_char where c_char ='abc    ';
 ---- ------------ ----------- 
| id | c_char     | c_varchar |
 ---- ------------ ----------- 
|  4 | abc        | abc       |
 ---- ------------ ----------- 
1 row in set (0.00 sec)

该结果是没有区别的。

3. 字符串长度查看函数

在MySQL中,字符串计算函数有如下几种,在使用是注意区分。

3.1 LENGTH 函数

用于返回字符串的字节数。对于英文字符,一个字符占用一个字节;而对于一些多字节字符(如UTF-8编码的中文字符),一个字符可能占用多个字节。

代码语言:javascript复制
SELECT LENGTH('Hello'); -- 返回 5
SELECT LENGTH('你好');   -- 返回 6(UTF-8编码中文字符占用3个字节)

3.2 CHAR_LENGTH 函数

用于返回字符串的字符数,而不是字节数。这意味着它会考虑字符的多字节性,确保正确地计算字符的数量。

代码语言:javascript复制
SELECT CHAR_LENGTH('Hello'); -- 返回 5
SELECT CHAR_LENGTH('你好');   -- 返回 2(两个中文字符)

3.3 CHARACTER_LENGTH 函数

与 CHAR_LENGTH 函数功能相似,也用于返回字符串的字符数。

代码语言:javascript复制
SELECT CHARACTER_LENGTH('Hello'); -- 返回 5
SELECT CHARACTER_LENGTH('你好');   -- 返回 2

3.4 BIT_LENGTH 函数

返回字符串的位数,而不是字节数或字符数。

代码语言:javascript复制
SELECT BIT_LENGTH('Hello'); -- 返回 40(5个英文字符,每个字符8位)
SELECT BIT_LENGTH('你好');   -- 返回 48(2个中文字符,每个字符24位)

4. 结语

了解CHAR和VARCHAR类型的差异对于有效地设计和管理MySQL数据库极为重要。选择合适的数据类型和了解它们的行为有助于避免意外的数据处理问题。同时,开发人员应根据具体需求和预期行为来选择合适的数据类型,以确保数据存储和检索的一致性和准确性。

往期精彩回顾

1. MySQL高可用之MHA集群部署

2. mysql8.0新增用户及加密规则修改的那些事

3. 比hive快10倍的大数据查询利器-- presto

4. 监控利器出鞘:Prometheus Grafana监控MySQL、Redis数据库

5. PostgreSQL主从复制--物理复制

6. MySQL传统点位复制在线转为GTID模式复制

7. MySQL敏感数据加密及解密

8. MySQL数据备份及还原(一)

9. MySQL数据备份及还原(二)

0 人点赞