DROP TABLE IF EXISTS `space_test`; CREATE TABLE `space_test` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键自增ID', `var_str` VARCHAR(64) NOT NULL DEFAULT '', `str` CHAR(64) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
INSERT INTO space_test(var_str, str) values ('abc', 'abc'), # no space (' abc', ' abc'), # one prefix space ('abc ', 'abc '), # one suffix space ('abc ', 'abc '); # four suffix spaces
DROP TABLE IF EXISTS `space_test`; CREATE TABLE `space_test` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键自增ID', `var_str` VARCHAR(64) NOT NULL DEFAULT '', `str` CHAR(64) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY (`var_str`), UNIQUE KEY (`str`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
INSERT INTO space_test(var_str, str) values ('abc', 'abc'); # no space
INSERT INTO space_test(var_str, str) values (' abc', '2'); # one varchar prefix space INSERT INTO space_test(var_str, str) values ('3', ' abc'); # one char prefix space
INSERT INTO space_test(var_str, str) values ('abc ', '4'); # one varchar suffix space INSERT INTO space_test(var_str, str) values ('5', 'abc '); # one char suffix space
INSERT INTO space_test(var_str, str) values ('abc ', '6'); # four varchar suffix space INSERT INTO space_test(var_str, str) values ('7' ,'abc '); # four char suffix space
mysql> INSERT INTO space_test(var_str, str) values ('abc', 'abc'); # no space Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO space_test(var_str, str) values (' abc', '2'); # one varchar prefix space Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO space_test(var_str, str) values ('3', ' abc'); # one char prefix space Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO space_test(var_str, str) values ('abc ', '4'); # one varchar suffix space Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO space_test(var_str, str) values ('5', 'abc '); # one char suffix space ERROR 1062 (23000): Duplicate entry 'abc' for key 'space_test.str'
mysql> INSERT INTO space_test(var_str, str) values ('abc ', '6'); # four varchar suffix space Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO space_test(var_str, str) values ('7' ,'abc '); # four char suffix space ERROR 1062 (23000): Duplicate entry 'abc' for key 'space_test.str'
在MySQL 8.0.x 中的现象和查询的结果也完全一致:
对于 varchar 的查询的逻辑不再去除尾部空格,而是采用精确匹配的方式;
对于 char 类型的查询会直接认为尾部不存在空格,并且仅会匹配尾部无空格的查询条件!
因此,在上面的插入中,varchar 类型可以成功插入,而 char 类型不可以!
最后,再来看看当字符串为主键时的场景;
字符串类型为主键场景
建表语句
varchar 类型的主键和 char 类型的主键对应的建表语句如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
-- varchar DROP TABLE IF EXISTS `space_test`; CREATE TABLE `space_test` ( `id` VARCHAR(64) NOT NULL, PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- char DROP TABLE IF EXISTS `space_test`; CREATE TABLE `space_test` ( `id` CHAR(64) NOT NULL, PRIMARY KEY (`id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
以及对应的写入数据的SQL语句:
1 2 3 4
INSERT INTO space_test(id) values ('abc'); # no space INSERT INTO space_test(id) values (' abc'); # one prefix space INSERT INTO space_test(id) values ('abc '); # one suffix space INSERT INTO space_test(id) values ('abc '); # four char suffix space
下面分 MySQL 版本来看;
MySQL 5.7.x中的表现
创建以 varchar 类型作为主键的表后,执行插入语句后的结果如下:
1 2 3 4 5 6 7 8 9 10 11
mysql> INSERT INTO space_test(id) values ('abc'); # no space Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO space_test(id) values (' abc'); # one prefix space Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO space_test(id) values ('abc '); # one suffix space ERROR 1062 (23000): Duplicate entry 'abc ' for key 'PRIMARY'
mysql> INSERT INTO space_test(id) values ('abc '); # four char suffix space ERROR 1062 (23000): Duplicate entry 'abc ' for key 'PRIMARY'
可以看到,和字段为唯一索引的情况是一致的:
尾部存在空格的字符串会和无空格的字符串冲突,从而无法被插入;
以 char 类型作为主键的表的结果和 varchar 的结果完全一致,这里不再赘述!
其实结果是显而易见的,因为在主键的语义中已经包括了 唯一索引 的概念;
MySQL 8.0.x中的表现
varchar类型作为主键
建表后结果如下:
1 2 3 4 5 6 7 8 9 10 11
mysql> INSERT INTO space_test(id) values ('abc'); # no space Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO space_test(id) values (' abc'); # one prefix space Query OK, 1 row affected (0.02 sec)
mysql> INSERT INTO space_test(id) values ('abc '); # one suffix space Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO space_test(id) values ('abc '); # four char suffix space Query OK, 1 row affected (0.01 sec)
和上面的结论也是一致的:
MySQL 8.0.x 中对于 varchar 的查询的逻辑不再去除尾部空格,而是采用精确匹配的方式;
可以查询到,此时有四条数据:
1 2 3 4 5 6 7 8 9 10 11
mysql> select * from space_test;
+---------+ | id | +---------+ | abc | | abc | | abc | | abc | +---------+ 4 rows in set (0.00 sec)
下面再来看 char 类型作为主键时的场景;
char类型作为主键
执行语句后结果如下:
1 2 3 4 5 6 7 8 9 10 11
mysql> INSERT INTO space_test(id) values ('abc'); # no space Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO space_test(id) values (' abc'); # one prefix space Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO space_test(id) values ('abc '); # one suffix space ERROR 1062 (23000): Duplicate entry 'abc' for key 'space_test.PRIMARY'
mysql> INSERT INTO space_test(id) values ('abc '); # four char suffix space ERROR 1062 (23000): Duplicate entry 'abc' for key 'space_test.PRIMARY'
也和上面的结论保持一致:
MySQL 8.0.x 中对于 char 类型的查询会直接认为尾部不存在空格,并且仅会匹配尾部无空格的查询条件!
If strict SQL mode is not enabled and you assign a value to a CHAR or VARCHAR column that exceeds the column’s maximum length, the value is truncated to fit and a warning is generated. For truncation of nonspace characters, you can cause an error to occur (rather than a warning) and suppress insertion of the value by using strict SQL mode. See Section 5.1.10, “Server SQL Modes”.
For VARCHAR columns, trailing spaces in excess of the column length are truncated prior to insertion and a warning is generated, regardless of the SQL mode in use. For CHAR columns, truncation of excess trailing spaces from inserted values is performed silently regardless of the SQL mode.
同时,对于 MySQL 而言,所有的字符集类型都是“填充空格”(All MySQL collations are of type PAD SPACE);
即:无论对于 CHAR、 VARCHAR、 还是 TEXT 类型而言,在比较时,都会忽略尾部的空格!
唯一需要区别的是,LIKE 关键字,因为这个场景下尾部空格的匹配对其非常重要!
原文:
All MySQL collations are of type PAD SPACE. This means that all CHAR, VARCHAR, and TEXT values are compared without regard to any trailing spaces. “Comparison” in this context does not include the LIKE pattern-matching operator, for which trailing spaces are significant.
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)
For those cases where trailing pad characters are stripped or comparisons ignore them, if a column has an index that requires unique values, inserting into the column values that differ only in number of trailing pad characters results in a duplicate-key error. For example, if a table contains 'a', an attempt to store 'a ' causes a duplicate-key error.
MySQL 8.0.x中新增内容
上面为 MySQL 5.7.x 文档中的内容,在 MySQL 8.0.x 中有了部分变化;
NO PAD类型
首先,引入了 NO PAD 类型的数据类型;
上面提到在老的 MySQL 版本中,所有的字符集类型都是“填充空格”(All MySQL collations are of type PAD SPACE);
MySQL collations have a pad attribute of PAD SPACE, other than Unicode collations based on UCA 9.0.0 and higher, which have a pad attribute of NO PAD. (see Section 10.10.1, “Unicode Character Sets”).
To determine the pad attribute for a collation, use the INFORMATION_SCHEMACOLLATIONS table, which has a PAD_ATTRIBUTE column.
非二进制字符串(nonbinary strings)比较变更
在上面的基础之上:
对于非二进制字符串(包括,CHAR, VARCHAR 和 TEXT 类型)而言,字符串的校对填充属性(collation pad attribute) 决定了在比较时是否处理字符串在尾部的空格:
NO PAD 类型会在比较时计入尾部的空格;
而 PAD SPACE 则不会在比较时计入尾部的空格;
原文:
For nonbinary strings (CHAR, VARCHAR, and TEXT values), the string collation pad attribute determines treatment in comparisons of trailing spaces at the end of strings. NO PAD collations treat trailing spaces as significant in comparisons, like any other character. PAD SPACE collations treat trailing spaces as insignificant in comparisons; strings are compared without regard to trailing spaces. See Trailing Space Handling in Comparisons. The server SQL mode has no effect on comparison behavior with respect to trailing spaces.