MySQL错误:没有密钥长度的密钥规范

我有一个主键是varchar(255)的表。 有些情况已经出现,255个字符是不够的。 我试图将字段更改为文本,但出现以下错误:

BLOB/TEXT column 'message_id' used in key specification without a key length 

我怎样才能解决这个问题?

编辑:我也应该指出,这个表具有多列的复合主键。

发生错误是因为MySQL只能索引BLOB或TEXT列的前N个字符。 所以错误主要发生在TEXT或BLOB的字段/列types或者属于TEXTBLOBtypes(如TINYBLOBMEDIUMBLOBLONGBLOBTINYTEXTMEDIUMTEXTLONGTEXT ,您尝试创build主键或索引。 对于没有长度值的完整BLOBTEXT ,MySQL不能保证列的唯一性,因为它的variables和dynamic大小。 因此,当使用BLOBTEXTtypes作为索引时,必须提供N的值,以便MySQL可以确定密钥长度。 但是,MySQL不支持TEXTBLOB上的密钥长度限制。 TEXT(88)根本不起作用。

当您尝试将表列从non-TEXTnon-BLOBtypes(例如VARCHARENUM转换为TEXTBLOBtypes(该列已被定义为唯一约束或索引)时,该错误也会popup。 Alter Table SQL命令将失败。

解决此问题的方法是从索引或唯一约束中删除TEXTBLOB列或将另一个字段设置为主键。 如果你不能这样做,并且想对TEXTBLOB列设置限制,请尝试使用VARCHARtypes并在其上放置一个长度限制。 默认情况下, VARCHAR被限制为最多255个字符,并且它的限制必须在声明后的括号内隐式指定,即VARCHAR(200)将其限制为200个字符长。

有时,即使您的表中不使用TEXTBLOB相关types,也可能会出现错误1170。 它发生在你指定VARCHAR列作为主键的情况下,但错误地设置了它的长度或字符大小。 VARCHAR最多只能接受256个字符,所以像VARCHAR(512)这样的任何东西都会强制MySQL将VARCHAR(512)自动转换为SMALLTEXT数据types,如果该列被用作主关键或唯一或不唯一的索引。 要解决此问题,请将小于256的数字指定为VARCHAR字段的大小。

参考: MySQL错误1170(42000):在没有密钥长度的密钥规范中使用的BLOB / TEXT列

你应该定义你想索引的TEXT列的哪个前导部分。

InnoDB每个索引键有768个字节的限制,您将无法创build比这更长的索引。

这将工作正常:

 CREATE TABLE t_length ( mydata TEXT NOT NULL, KEY ix_length_mydata (mydata(255))) ENGINE=InnoDB; 

请注意,密钥大小的最大值取决于列字符集。 像LATIN1这样的单字节字符集有767 255字符, UTF8只有255字符( MySQL只使用BMP ,每个字符最多需要3个字节)

如果您需要将整列作为PRIMARY KEY ,请计算SHA1MD5散列值并将其用作PRIMARY KEY

您可以在alter table request中指定密钥长度,如下所示:

 alter table authors ADD UNIQUE(name_first(20), name_second(20)); 

MySQL不允许将BLOBTEXT和long VARCHAR列的全部值编入索引,因为它们包含的数据可能很大,并且隐式DB索引将会很大,这意味着索引没有任何好处。

MySQL要求你定义前N个字符进行索引,诀窍是select一个数字N足够长,以提供良好的select性,但足够短,以节省空间。 前缀应该足够长,以使索引几乎与索引整列时的索引一样有用。

在我们进一步讨论之前让我们定义一些重要的术语 索引select性总的不同索引值与总行数的比率。 这里是testing表的一个例子:

 +-----+-----------+ | id | value | +-----+-----------+ | 1 | abc | | 2 | abd | | 3 | adg | +-----+-----------+ 

如果我们仅索引第一个字符(N = 1),那么索引表将如下表所示:

 +---------------+-----------+ | indexedValue | rows | +---------------+-----------+ | a | 1,2,3 | +---------------+-----------+ 

在这种情况下,指数select性等于IS = 1/3 = 0.33。

现在让我们看看如果将索引字符的数量增加到两个(N = 2)会发生什么情况。

 +---------------+-----------+ | indexedValue | rows | +---------------+-----------+ | ab | 1,2 | | ad | 3 | +---------------+-----------+ 

在这种情况下,IS = 2/3 = 0.66这意味着我们增加了指数select性,但是我们也增加了指数的大小。 诀窍是要find最小的数量N,这将导致最大的索引select性

有两种方法可以为数据库表进行计算。 我将在这个数据库转储进行演示。

比方说,我们希望在表雇员last_name添加到索引,我们要定义最小的数量N将产生最佳的索引select性。

首先让我们找出最常见的姓氏:

 select count(*) as cnt, last_name from employees group by employees.last_name order by cnt +-----+-------------+ | cnt | last_name | +-----+-------------+ | 226 | Baba | | 223 | Coorg | | 223 | Gelosh | | 222 | Farris | | 222 | Sudbeck | | 221 | Adachi | | 220 | Osgood | | 218 | Neiman | | 218 | Mandell | | 218 | Masada | | 217 | Boudaillier | | 217 | Wendorf | | 216 | Pettis | | 216 | Solares | | 216 | Mahnke | +-----+-------------+ 15 rows in set (0.64 sec) 

正如你所看到的,姓氏巴巴是最常见的一个。 现在我们将find最常出现的last_name前缀,以五个字母的前缀开头。

 +-----+--------+ | cnt | prefix | +-----+--------+ | 794 | Schaa | | 758 | Mande | | 711 | Schwa | | 562 | Angel | | 561 | Gecse | | 555 | Delgr | | 550 | Berna | | 547 | Peter | | 543 | Cappe | | 539 | Stran | | 534 | Canna | | 485 | Georg | | 417 | Neima | | 398 | Petti | | 398 | Duclo | +-----+--------+ 15 rows in set (0.55 sec) 

每个前缀的出现次数要多得多,这意味着我们必须增加数字N,直到数值几乎与前面的例子相同。

这里是N = 9的结果

 select count(*) as cnt, left(last_name,9) as prefix from employees group by prefix order by cnt desc limit 0,15; +-----+-----------+ | cnt | prefix | +-----+-----------+ | 336 | Schwartzb | | 226 | Baba | | 223 | Coorg | | 223 | Gelosh | | 222 | Sudbeck | | 222 | Farris | | 221 | Adachi | | 220 | Osgood | | 218 | Mandell | | 218 | Neiman | | 218 | Masada | | 217 | Wendorf | | 217 | Boudailli | | 216 | Cummings | | 216 | Pettis | +-----+-----------+ 

这里是N = 10的结果。

 +-----+------------+ | cnt | prefix | +-----+------------+ | 226 | Baba | | 223 | Coorg | | 223 | Gelosh | | 222 | Sudbeck | | 222 | Farris | | 221 | Adachi | | 220 | Osgood | | 218 | Mandell | | 218 | Neiman | | 218 | Masada | | 217 | Wendorf | | 217 | Boudaillie | | 216 | Cummings | | 216 | Pettis | | 216 | Solares | +-----+------------+ 15 rows in set (0.56 sec) 

这是非常好的结果。 这意味着我们可以在列名last_name上build立索引,索引只有前10个字符。 在表定义列中, last_name定义为VARCHAR(16) ,这意味着我们已经为每个条目保存了6个字节(如果在姓氏中有UTF8字符,则保留更多)。 在这个表中,有1637个不同的值乘以6个字节大约是9KB,想象一下,如果我们的表包含数百万行,这个数字将如何增长。

你可以阅读其他的方法来计算我的post中的N的数量在MySQL中的前缀索引 。

 alter table authors ADD UNIQUE(name_first(767), name_second(767)); 

注意767是限制哪些MySQL处理blob / text索引时将索引列的字符数

参考: http : //dev.mysql.com/doc/refman/5.7/en/innodb-restrictions.html

不要有长的值作为主键。 这会破坏你的performance。 请参阅mysql手册第13.6.13节“InnoDB性能调优和故障排除”。

相反,有一个代理诠释键作为主要(与auto_increment),你的长键作为辅助唯一。

当255个字符不够时,添加另一个varChar(255)列(默认为空string不为空)来保存溢出,并将此PK更改为使用两个列。 这听起来不像是一个精心devise的数据库模式,但我build议让数据build模师看看你有什么样的观点来重构它以获得更多的规范化。

处理这个问题的另一个很好的方法是创build没有唯一约束的TEXT字段,并添加一个唯一的包含TEXT字段的摘要(MD5,SHA1等)的兄弟VARCHAR字段。 插入或更新TEXT字段时,计算整个TEXT字段的摘要并将其存储在整个TEXT字段中,然后在整个TEXT字段(而不是某个引导部分)上具有可快速search的唯一性约束。

另外,如果要在此字段中使用索引,则应该使用MyISAM存储引擎和FULLTEXT索引types。

问题的解决scheme是,在CREATE TABLE语句中,可以在列创build定义之后添加约束UNIQUE ( problemtextfield(300) ) ,以便为TEXT字段指定300字符的key长度。 然后, problemtextfield TEXT字段TEXT字段的前300字符将需要是唯一的,之后的任何差异都将被忽略。