你不知道的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 模式。
'PAD_CHAR_TO_FULL_LENGTH'; SET sql_mode =
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 中的 varchar 选择 255 以外的任何其他长度?