我的项目需要从用户开始输入,左边的间距和单词右边的间距,例如'apple'。如果用户键入“apple”或“apple”,无论是单词的左侧还是右侧是一个空格还是多个空格,我都需要以这种方式存储它。
此字段具有唯一属性,但我尝试在左侧插入带间距的单词,并且它可以正常工作。但是当我试图在右边插入带有间距的单词时,它会修剪单词右边的所有间距。
所以我想在间距之后在单词的右边添加一个特殊字符。但我希望有更好的解决方案来解决这个问题。
CREATE TABLE strings
( id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
string varchar(255) COLLATE utf8_bin NOT NULL,
created_ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id), UNIQUE KEY string (string) )
ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
答案 0 :(得分:7)
问题是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
或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。不确定是否有人想这样做,但如果你这样做,请告诉我;)
编辑:同时MySQL根据https://dev.mysql.com/doc/refman/8.0/en/char.html具有NO PAD类型的排序规则:
大多数MySQL排序规则都具有PAD SPACE的pad属性。例外情况是基于UCA 9.0.0及更高版本的Unicode排序规则,其pad属性为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';
答案 1 :(得分:3)
这与CHAR vs VARCHAR无关。在字符串比较时,SQL Server不考虑尾随空格,这在检查唯一键约束时也会应用。因此,并不是您不能使用尾随空格插入值,但是一旦插入,就无法插入具有更多或更少空格的其他值。
作为问题的解决方案,您可以添加一个保持字符串长度的列,并将长度和字符串值作为复合唯一键约束。
在SQL Server 2012中,您甚至可以将长度列设为计算列,这样您就不必担心该值了。有关SQL Server 2012的示例,请参阅http://sqlfiddle.com/#!6/32e94。(我打赌在MySQL中可以采用类似的方法。)
答案 2 :(得分:1)
您可能需要了解VARCHAR和CHAR类型之间的差异。
当存储CHAR值时,它们用空格填充到指定的长度。检索CHAR值时,除非启用PAD_CHAR_TO_FULL_LENGTH SQL模式,否则将删除尾随空格。
对于VARCHAR列,在插入之前会截断超过列长度的尾随空格,并且无论使用何种SQL模式,都会生成警告。对于CHAR列,无论SQL模式如何,都会以静默方式截断插入值中的多余尾随空格。
VARCHAR值在存储时不会填充。存储和检索值时,将保留尾随空格,符合标准SQL。
结论:如果要在文本字符串的右侧保留空格,请使用CHAR类型(而不是VARCHAR)。
答案 3 :(得分:0)
感谢@kennethc。他的回答对我有用。 将字符串长度字段添加到表和唯一键。
CREATE TABLE strings
( id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
string varchar(255) COLLATE utf8_bin NOT NULL,
created_ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
string_length int(3),
PRIMARY KEY (id), UNIQUE KEY string (string,string_length) )
ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
在MySQL中,可以使用以下几个触发器来更新字符串长度字段:
CREATE TRIGGER `string_length_insert` BEFORE INSERT ON `strings` FOR EACH ROW SET NEW.string_length = char_length(NEW.string);
CREATE TRIGGER `string_length_update` BEFORE UPDATE ON `strings` FOR EACH ROW SET NEW.string_length = char_length(NEW.string);