你不知道的mysql varchar

Posted 程序与人生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你不知道的mysql varchar相关的知识,希望对你有一定的参考价值。

`char`和 `varchar`类型很相似,但是在存储、最大字符长度、是否保留尾部空格等方面略有不同。


`char`和`varchar`都声明了一个你希望存储的字符串的最大长度,比如char(30)类型的字段最大多可以存储30个字符。


`char`类型的字段长度是固定的,长度为0-255之间,`char`类型字段存储时,如果长度不足你定义的长度,则mysql会默认使用空格填充到字符串右边,以使字符串长度和你定义的长度一样。而当需要获取`char`类型字段的值时,系统又会自动将尾部空格都去掉后再返回。当然如果你不希望去掉尾部空格,你可以开启PAD_CHAR_TO_FULL_LENGTH 模式。

mysql> SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';Query OK, 0 rows affected (0.00 sec)

`varchar`类型字段是变长类型的字符串,长度为0-65535之间。`varchar`的最大有效长度受限于每行的最大空间(65535字节,所有列共享这个空间)和每个字符使用的空间大小。参考[表的行大小和列数限制](https://dev.mysql.com/doc/refman/8.0/en/column-count-limit.html)

(译注:这里一定要分清长度和大小的概念,比如字符串"123你好"它的长度为5,但是他所占字节数就要依赖于字符串编码了,如果是utf8,则中文字符就占用三个字节了,占用的字节数为12所以varchar(65535)可能存的字符长度要远小于65535

相较于`char`,`varchar`还会在数据前面加1-2字节的额外长度,用来记录数据的字节数。如果数据大小不超过255字节,则用一个字节存长度就好,否则需要两个字节。


如果没有开启strict模式,则当数据超过`char`或者`varchar`类型的最大长度时,数据就会被截掉并且触发一条告警。如果不希望数据隐式的被截断,你也可以通过打开strict模式,从而让系统抛出错误。参考[SQL模式](https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html)


对于`varchar`,超过长度的尾部空格会被截断后再插入并且生成一条告警,不管是是否使用strict模式。对于`char`,超过长度的尾部空格则在插入后被自动截断,不管是是否使用strict模式。


`varchar`列数据不会自动填充,尾部空格也会保存入库及被取出,保持和标准SQL一致。


下表展示了char(4)和varchar(4)两列在存储方面的一些差别。

数据 char(4) 存储大小 varchar(4) 存储大小
‘’ ‘ ’ 4字节 ‘’ 1字节
‘ab’ ‘ab ’ 4字节 ‘ab’ 3字节
‘abcd’ ‘abcd’ 4字节 ‘abcd’ 5字节
‘abcdefh’ ‘abcd ’ 4字节 ‘abcd’ 5字节

上面最后一行数据只有在非strict模式下才能插入成功,否则将会得到一条插入失败的报错信息。


InnoDB将长度大于或等于768字节的固定长度字段编码为可变长度字段,可以将其存储在页外。比如如果字符最大字节数超过3的话(utf8mb4编码最大为4字节),char(255)的存储大小可能就超过768字节了。


同一个字符串,存到char(4)和varchar(4)列,取出来的值可能就不一样了,因为char列的尾部空格就被自动删除了。如下举例:


mysql> CREATE TABLE vc (v VARCHAR(4), c CHAR(4));Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO vc VALUES ('ab ', 'ab ');Query OK, 1 row affected (0.00 sec)
mysql> SELECT CONCAT('(', v, ')'), CONCAT('(', c, ')') FROM vc;+---------------------+---------------------+| CONCAT('(', v, ')') | CONCAT('(', c, ')') |+---------------------+---------------------+| (ab ) | (ab) |+---------------------+---------------------+1 row in set (0.06 sec)

`char`和`varchar`列的存储和比较都是基于列定义的字符集的校对规则的。


大多数MySQL的校对规则都有一个填充属性`PAD SPACE`。只有基于UCA9.0.0或者更高版本的Unicode字符集不一样,它的属性名是`NO PAD`。


如果需要了解校对规则的属性,可以查看表 INFORMATION_SCHEMA.COLLATIONS。


当与非二进制字符串(char, varchar, text)进行比较时,填充属性决定了尾部空格的处理方式。`NO PAD`校对规则会将尾部空格进行比较,`PAD SPACE`校对规则则会忽略尾部空格进行比较。但是通过LIKE关键词进行模式匹配的时候,还是会比较空格的。


mysql> CREATE TABLE names (myname CHAR(10));Query OK, 0 rows affected (0.03 sec)
mysql> INSERT INTO names VALUES ('Jones');Query OK, 1 row affected (0.00 sec)
mysql> SELECT myname = 'Jones', myname = 'Jones ' FROM names;+--------------------+--------------------+| myname = 'Jones' | myname = 'Jones ' |+--------------------+--------------------+| 1 | 1 |+--------------------+--------------------+1 row in set (0.00 sec)
mysql> SELECT myname LIKE 'Jones', myname LIKE 'Jones ' FROM names;+-----------------------+-----------------------+| myname LIKE 'Jones' | myname LIKE 'Jones ' |+-----------------------+-----------------------+| 1 | 0 |+-----------------------+-----------------------+1 row in set (0.00 sec)

这个在所有MySQL版本以及任何SQL模式下都是通用的。


对于这些尾部填充空格被删除或者比较时被忽略的情形,如果某列要求为唯一索引,当插入只有尾部空格不一样的数据时,就会报重复列的错误。比如表中已经有一列的内容为'a',  当想要插入'a  '时就报错了。


以上是关于你不知道的mysql varchar的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 系列 你不知道的数据库操作

关于MySQL退出命令,还有你不知道的一种操作

为啥我应该为 MySQL 中的 varchar 选择 255 以外的任何其他长度?

Go 语言 10 岁了!这里有你不知道的 Go 的成长历程

MySQL 系列你不知道的 视图触发器存储过程函数事务索引语句

Go 语言也 10 岁了!这里或许有你不知道的 Go 的成长历程