MySQL错误:没有密钥长度的密钥规范
我有一个主键是varchar(255)的表。 有些情况已经出现,255个字符是不够的。 我试图将字段更改为文本,但出现以下错误:
BLOB/TEXT column 'message_id' used in key specification without a key length
我怎样才能解决这个问题?
编辑:我也应该指出,这个表具有多列的复合主键。
发生错误是因为MySQL只能索引BLOB或TEXT
列的前N个字符。 所以错误主要发生在TEXT
或BLOB的字段/列types或者属于TEXT
或BLOB
types(如TINYBLOB
, MEDIUMBLOB
, LONGBLOB
, TINYTEXT
, MEDIUMTEXT
和LONGTEXT
,您尝试创build主键或索引。 对于没有长度值的完整BLOB
或TEXT
,MySQL不能保证列的唯一性,因为它的variables和dynamic大小。 因此,当使用BLOB
或TEXT
types作为索引时,必须提供N的值,以便MySQL可以确定密钥长度。 但是,MySQL不支持TEXT
或BLOB
上的密钥长度限制。 TEXT(88)
根本不起作用。
当您尝试将表列从non-TEXT
和non-BLOB
types(例如VARCHAR
和ENUM
转换为TEXT
或BLOB
types(该列已被定义为唯一约束或索引)时,该错误也会popup。 Alter Table SQL命令将失败。
解决此问题的方法是从索引或唯一约束中删除TEXT
或BLOB
列或将另一个字段设置为主键。 如果你不能这样做,并且想对TEXT
或BLOB
列设置限制,请尝试使用VARCHAR
types并在其上放置一个长度限制。 默认情况下, VARCHAR
被限制为最多255个字符,并且它的限制必须在声明后的括号内隐式指定,即VARCHAR(200)
将其限制为200个字符长。
有时,即使您的表中不使用TEXT
或BLOB
相关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
,请计算SHA1
或MD5
散列值并将其用作PRIMARY KEY
。
您可以在alter table request中指定密钥长度,如下所示:
alter table authors ADD UNIQUE(name_first(20), name_second(20));
MySQL不允许将BLOB
, TEXT
和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
字符将需要是唯一的,之后的任何差异都将被忽略。