问题是 MySQL 在进行字符串比较时会忽略尾随空格。见
http://dev.mysql.com/doc/refman/5.7/en/char.html
所有 MySQL 排序规则都是 PADSPACE 类型。这意味着在比较 MySQL 中的所有 CHAR、VARCHAR 和 TEXT 值时,不考虑任何尾随空格。
...
对于去除尾随填充字符或比较忽略它们的情况,如果列具有需要唯一值的索引,则将仅在尾随填充字符数量上不同的值插入到列中将导致重复键错误。例如,如果表包含“a”,则尝试存储“a”会导致重复键错误。
(此信息适用于 5.7;对于 8.0,此信息已更改,请参见下文)
运算符部分like
给出了这种行为的一个例子(并表明它like
确实尊重尾随空格):
mysql> SELECT 'a' = 'a ', 'a' LIKE 'a ';
+------------+---------------+
| 'a' = 'a ' | 'a' LIKE 'a ' |
+------------+---------------+
| 1 | 0 |
+------------+---------------+
1 row in set (0.00 sec)
不幸的是,UNIQUE
索引似乎使用标准字符串比较来检查是否已经存在这样的值,因此忽略了尾随空格。这与使用VARCHAR
or无关CHAR
,在这两种情况下插入都被拒绝,因为唯一检查失败。如果有办法使用like
语义进行UNIQUE
检查,那么我不知道。
您可以做的是将值存储为VARBINARY
:
mysql> create table test_ws ( `value` varbinary(255) UNIQUE );
Query OK, 0 rows affected (0.13 sec)
mysql> insert into test_ws (`value`) VALUES ('a');
Query OK, 1 row affected (0.08 sec)
mysql> insert into test_ws (`value`) VALUES ('a ');
Query OK, 1 row affected (0.06 sec)
mysql> SELECT CONCAT( '(', value, ')' ) FROM test_ws;
+---------------------------+
| CONCAT( '(', value, ')' ) |
+---------------------------+
| (a) |
| (a ) |
+---------------------------+
2 rows in set (0.00 sec)
您最好不要在此列上按字母顺序排序,因为排序将发生在字节值上,这不是用户所期望的(大多数用户,无论如何)。
另一种方法是修补 MySQL 并编写您自己的排序规则,该排序规则类型为 NO PAD。不确定是否有人想这样做,但如果你这样做,请告诉我;)
编辑:根据https://dev.mysql.com/doc/refman/8.0/en/char.html,同时 MySQL 有类型为 NO PAD 的排序规则:
大多数 MySQL 排序规则都有一个 PAD SPACE 的 pad 属性。例外是基于 UCA 9.0.0 和更高版本的 Unicode 排序规则,其填充属性为 NO PAD。
和https://dev.mysql.com/doc/refman/8.0/en/charset-unicode-sets.html
基于 4.0.0 之后的 UCA 版本的 Unicode 排序规则在排序规则名称中包含该版本。因此,utf8mb4_unicode_520_ci 基于 UCA 5.2.0 权重键,而 utf8mb4_0900_ai_ci 基于 UCA 9.0.0 权重键。
因此,如果您尝试:
create table test_ws ( `value` varbinary(255) UNIQUE )
character set utf8mb4 collate utf8mb4_0900_ai_ci;
您可以插入带有或不带有尾随空格的值
您可以找到所有可用的 NO PAD 归类:
show collation where Pad_attribute='NO PAD';