具有唯一字段的MySQL数据库忽略了结尾空格

具有唯一字段的MySQL数据库忽略了结尾空格,mysql,mysql5,Mysql,Mysql5,我的项目需要用户以单词左边的空格和右边的空格开始输入,例如“apple”。如果用户键入“apple”或“apple”,无论是单词左侧或右侧的一个空格还是多个空格,我都需要以这种方式存储它 此字段具有唯一属性,但我尝试在左侧插入带空格的单词,效果很好。但当我尝试插入右边有空格的单词时,它会将单词右边的所有空格都剪掉 所以我想在单词右边的空格后面加一个特殊的字符。但我希望这个问题有更好的解决办法 CREATE TABLE strings ( id bigint(20) unsigned NOT NU

我的项目需要用户以单词左边的空格和右边的空格开始输入,例如“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

您可能需要了解VARCHAR和CHAR类型之间的差异

存储CHAR值时,会使用空格右键填充到指定的长度。检索CHAR值时,除非启用PAD_CHAR_TO_FULL_LENGTH SQL模式,否则将删除尾随空格

对于VARCHAR列,无论使用何种SQL模式,超过列长度的尾随空格在插入之前都会被截断,并生成警告。对于CHAR列,不管SQL模式如何,都会以静默方式执行从插入值中截断多余尾随空格的操作

VARCHAR值在存储时不进行填充。根据标准SQL,在存储和检索值时保留尾随空格


结论:如果要在文本字符串的右侧保留空格,请使用CHAR类型(而不是VARCHAR)。

这不是CHAR与VARCHAR的关系。SQLServer在字符串比较时不考虑尾随空格,这也适用于检查唯一的键约束。所以,这并不是说不能用尾随空格插入值,而是一旦插入,就不能用更多或更少的空格插入另一个值

作为问题的解决方案,可以添加保持字符串长度的列,并将长度和字符串值作为复合唯一键约束


在中,甚至可以将length列作为计算列,这样就不必担心该值。有关SQL Server 2012的示例,请参见。(我打赌MySQL中也可能有类似的功能。)

问题是MySQL在进行字符串比较时会忽略尾随空格。看见

所有MySQL排序规则都是PADSPACE类型。这意味着MySQL中的所有CHAR、VARCHAR和TEXT值都会进行比较,而不考虑任何尾随空格

对于删除尾随字符或比较忽略尾随字符的情况,如果列的索引需要唯一值,则在列中插入仅在尾随字符数上不同的值将导致重复的键错误。例如,如果表包含“a”,则尝试存储“a”会导致重复键错误

(此信息适用于5.7;对于8.0,此信息已更改,请参见下文)

like
操作符的部分给出了此行为的示例(并显示
like
确实尊重尾随空格):

不幸的是,
UNIQUE
索引似乎使用标准字符串比较来检查是否已经存在这样的值,因此忽略了尾随空格。 这与使用
VARCHAR
CHAR
无关,在这两种情况下,插入都被拒绝,因为唯一性检查失败。如果有一种方法可以使用
like
语义进行
唯一的检查,那么我不知道

您可以将该值存储为
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具有类型为NO PAD的排序规则,根据:

大多数MySQL排序规则都有pad属性pad SPACE。例外情况是基于UCA 9.0.0及更高版本的Unicode排序规则,其pad属性为NO pad

基于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;
可以插入带有和不带尾随空格的值

您可以通过以下方式找到所有可用的无填充排序规则:

 show collation where Pad_attribute='NO PAD';

感谢@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);

问题是关于
唯一
约束,该约束忽略
字符
变量字符
文本
列上的尾随空格。我认为按照我的建议将字段类型更改为字符可以解决问题。是的,问题是我不知道提供了多少空格,可以是一个或多个。因此,它可能提供也可能不提供准确的空白计数。另一个问题是,字段类型为unique属性,这意味着如果输入
apple
apple
。它会出错。正如我在上面的回答中所解释的,如果您将字段类型更改为CHAR,您将解决以下问题:所有尾随空格都保留在CHAR字段中。以及唯一VAL的问题。您可以发布您的表架构吗?
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);