CHAR与VARCHAR详解

前言:

前面写过一篇介绍int类型的文章,一直想写一篇介绍字符串字段类型的文章,一直拖着也没思路要怎么下手。最近多关注了下这方面的文章,决定还是把拖了好久的文章了结了吧。本篇文章主要会介绍字符串类型char及varchar的用法及区别。

本文实验环境为MySQL 5.7.23版本,存储引擎为Innodb,sql_mode采用严格模式,字符集是utf8。

▍1.CHAR类型介绍

我们平时使用char类型定义字段时,往往会指定其长度M,即char(M)。其实M指的是字符数,即这个字段最多存储多少个字符,M可不指定,默认为1,范围是[0,255],单个字母、数字、中文等都是占用一个字符。utf8字符集下一个中文字符占用3个字节。下面我们简单测试下:

# 假设以如下建表语句创建测试表
CREATE TABLE `char_tb1` (
  `col1` char DEFAULT NULL,
  `col2` char(5) DEFAULT NULL,
  `col3` char(10) DEFAULT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

# 进入数据库查询建表语句如下 发现char(M) M可不指定,默认为1
mysql> show create table char_tb1\G
*************************** 1. row ***************************
       Table: char_tb1
Create Table: CREATE TABLE `char_tb1` (
  `col1` char(1) DEFAULT NULL,
  `col2` char(5) DEFAULT NULL,
  `col3` char(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

# 插入数据 可以看出M表示保存的最大字符数,字母、数字、中文等都是占用一个字符
mysql> insert into char_tb1 (col1) values ('a'),('1'),('王'),(']');
Query OK, 4 rows affected (0.01 sec)
mysql> insert into char_tb1 (col1) values ('aa'),('12');
ERROR 1406 (22001): Data too long for column 'col1' at row 1
mysql> select * from char_tb1;
+------+------+------+
| col1 | col2 | col3 |
+------+------+------+
| a    | NULL | NULL |
| 1    | NULL | NULL |
| 王   | NULL | NULL |
| ]    | NULL | NULL |
+------+------+------+
4 rows in set (0.00 sec)
mysql> insert into char_tb1 (col2) values ('abcd'),('王-123'),('^*123'),('12'),('一二三四五');
Query OK, 5 rows affected (0.01 sec)
mysql> insert into char_tb1 (col2) values ('abcdef');
ERROR 1406 (22001): Data too long for column 'col2' at row 1
mysql> select * from char_tb1;
+------+-----------------+------+
| col1 | col2            | col3 |
+------+-----------------+------+
| a    | NULL            | NULL |
| 1    | NULL            | NULL |
| 王   | NULL            | NULL |
| ]    | NULL            | NULL |
| NULL | abcd            | NULL |
| NULL | 王-123          | NULL |
| NULL | ^*123           | NULL |
| NULL | 12              | NULL |
| NULL | 一二三四五      | NULL |
+------+-----------------+------+
9 rows in set (0.00 sec)

# 下面测试发现M的范围是[0,255] 
mysql> alter table char_tb1 add column col4 char(0);
Query OK, 0 rows affected (0.10 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table char_tb1 add column col5 char(255);
Query OK, 0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table char_tb1 add column col5 char(256);
ERROR 1074 (42000): Column length too big for column 'col5' (max = 255); use BLOB or TEXT instead

▍2.VARCHAR类型介绍

同样的,varchar(M)中的的M表示保存的最大字符数,单个字母、数字、中文等都是占用一个字符。varchar可存储的长度范围为0-65535字节,此外,varchar需要使用1或者2个额外字节记录字符串的长度:如果列的最大长度小于或等于255字节,则只使用1个字节表示,否则使用2个字节。对于Innodb引擎,utf8字符集来说,单个中文字符占用3个字节,所以varchar(M)中的M最大不会超过21845,即M的范围是[0,21845),并且M必须指定。另外MySQL规定:单个字段长度不大于65535字节;单行最大限制为65535,这里不包括TEXT、BLOB字段。即单张表中的所有varchar字段定义的长度之和不能大于65535,所以并不是所有varchar(M)字段中的M都可以取到21844,下面我们来验证下:

# 假设以如下建表语句创建测试表
CREATE TABLE `varchar_tb1` (
  `col1` varchar(0) DEFAULT NULL
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

# 查看建表语句 增加字段 发现M必须指定
mysql> show create table varchar_tb1\G
*************************** 1. row ***************************
       Table: varchar_tb1
Create Table: CREATE TABLE `varchar_tb1` (
  `col1` varchar(0) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> alter table varchar_tb1 add column col2 varchar;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1

# 下面测试证明M最大可取到21844
mysql> CREATE TABLE `varchar_tb2` (col1 varchar(21844));
Query OK, 0 rows affected (0.04 sec)

mysql> CREATE TABLE `varchar_tb3` (col1 varchar(218445));
ERROR 1074 (42000): Column length too big for column 'col1' (max = 21845); use BLOB or TEXT instead

# 下面测试证明单行最大限制为65535字节
mysql> CREATE TABLE `varchar_tb3` (col1 varchar(10));
Query OK, 0 rows affected (0.04 sec)

mysql> alter table varchar_tb3 add column col2 varchar(21844);
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
mysql> alter table varchar_tb3 add column col2 varchar(21834);
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
mysql> alter table varchar_tb3 add column col2 varchar(21833);
Query OK, 0 rows affected (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table varchar_tb3\G
*************************** 1. row ***************************
       Table: varchar_tb3
Create Table: CREATE TABLE `varchar_tb3` (
  `col1` varchar(10) DEFAULT NULL,
  `col2` varchar(21833) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

▍3.CHAR与VARCHAR比较

CHAR类型是定长的,MySQL总是根据定义的字符串长度分配足够的空间。当保存CHAR值时,在它们的右边填充空格以达到指定的长度,当检索到CHAR值时,尾部的空格被删除掉。

VARCHAR类型用于存储可变长字符串,存储时,如果字符没有达到定义的位数,也不会在后面补空格。但是,由于行是变长的,在UPDATE时可能使行变得比原来更长,这就导致需要做额外的工作。如果一个行占用的空间增长,并且在页内没有更多的空间可以存储,在这种情况下InnoDB需要分裂页来使行可以放进页内,这样会增加碎片。

下面简单总结下CHAR与VARCHAR字段类型的适用场景:

CHAR适合存储很短的字符串,或者所有值都接近同一个长度。例如,CHAR非常适合存储密码的MD5值,因为这是一个定长的值。对于经常变更的数据,CHAR也比VARCHAR更好,因为定长的CHAR类型不容易产生碎片。对于非常短的列,CHAR比VARCHAR在存储空间上也更有效率。例如用CHAR(1)来存储只有Y和N的值,如果采用单字节字符集只需要一个字节,但是VARCHAR(1)却需要两个字节,因为还有一个记录长度的额外字节。

下面这些情况下使用VARCHAR是合适的:字符串很长或者所要存储的字符串长短不一,差别很大;字符串列的最大长度比平均长度大得多;列的更新很少,所以碎片不是问题。

额外说明下,我们在定义字段最大长度时应该按需分配,提前做好预估。特别是对于VARCHAR字段,有人认为反正VARCHAR数据类型是根据实际的需要来分配长度的,还不如给大一点呢。但事实不是这样的,比如现在需要存储一个地址信息,根据评估,只要使用100个字符就可以了,我们可以使用VARCHAR(100)或VARCHAR(200)来存储,虽然它们用来存储90个字符的数据,其存储空间相同,但是对于内存的消耗是不同的。更长的列会消耗更多的内存,因为MySQL通常会分配固定大小的内存块来保存内部值,尤其是使用内存临时表进行排列或者操作时会特别糟糕。所以我们在分配VARCHAR数据类型时仍然不能够太过于慷慨。还是要评估实际需要的长度,然后选择一个最长的字段来设置字符长度。如果为了考虑冗余,可以留10%左右的字符长度。千万不能认为VARCHAR是根据实际长度来分配存储空间,而随意的分配长度,或者说干脆使用最大的字符长度。

总结:

本文分别介绍了CHAR与VARCHAR字段类型的使用方法,并且给出了二者的对比以及适用场景。在实际生产情况,需要具体情况具体分析,合适的才是最好的,希望这篇文章能给到大家参考。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容