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

时间:2009-12-01 15:50:46

标签: mysql sql mysql-error-1170

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

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

我该如何解决这个问题?

编辑:我还应该指出这个表有一个包含多列的复合主键。

18 个答案:

答案 0 :(得分:505)

发生错误是因为MySQL只能索引BLOB或TEXT列的前N个字符。因此,如果字段/列类型为TEXT或BLOB,或者属于TEXTBLOB类型,例如TINYBLOBMEDIUMBLOB,则主要发生错误您尝试制作主键或索引的LONGBLOBTINYTEXTMEDIUMTEXTLONGTEXT。对于没有长度值的完整BLOBTEXT,MySQL无法保证列的唯一性,因为它具有可变和动态大小。因此,当使用BLOBTEXT类型作为索引时,必须提供N的值,以便MySQL可以确定密钥长度。但是,MySQL不支持TEXTBLOB的密钥长度限制。 TEXT(88)根本行不通。

当您尝试将表格列从non-TEXTnon-BLOB类型(例如VARCHARENUM转换为TEXT时,也会弹出错误BLOB类型,列已被定义为唯一约束或索引。 Alter Table SQL命令将失败。

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

有时,即使您未在表格中使用TEXTBLOB相关类型,也可能会出现错误1170。它发生在诸如将VARCHAR列指定为主键但错误地设置其长度或字符大小的情况下。 VARCHAR最多只能接受256个字符,因此VARCHAR(512)等任何内容都会强制MySQL将VARCHAR(512)自动转换为SMALLTEXT数据类型,该数据类型随后会因错误1170而失败如果列用作主键或唯一或非唯一索引,则为密钥长度。要解决此问题,请指定小于256的数字作为VARCHAR字段的大小。

参考:MySQL Error 1170 (42000): BLOB/TEXT Column Used in Key Specification Without a Key Length

答案 1 :(得分:76)

您应该定义要编入索引的TEXT列的哪个前导部分。

InnoDB每个索引键限制768个字节,您将无法创建超过该索引的索引。

这样可以正常工作:

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

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

如果您需要将整个列设为SHA1,请计算MD5PRIMARY KEY哈希并将其用作{{1}}。

答案 2 :(得分:55)

您可以在alter table请求中指定密钥长度,例如:

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

答案 3 :(得分:19)

MySQL不允许索引BLOBTEXT和长VARCHAR列的完整值,因为它们包含的数据可能很大,并且隐含的数据库索引会很大,这意味着索引没有任何好处

MySQL要求您定义要编入索引的前N个字符,其诀窍是选择一个足够长的数字N来提供良好的选择性,但要足够短以节省空间。前缀应足够长,以使索引几乎与索引整列时的索引一样有用。

在我们进一步讨论之前,让我们定义一些重要的术语。 索引选择性总不同索引值与总行数的比率。以下是测试表的一个示例:

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

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

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

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

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

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

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

您可以使用两种方法对数据库表进行计算。我将在this database dump上进行演示。

假设我们要将表 employees 中的列 last_name 添加到索引中,并且我们要定义最小的数字 N 将产生最佳的指数选择性。

首先让我们确定最常用的姓氏:

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)

如您所见,姓氏 Baba 是最常见的名称。现在我们将找到最常出现的 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上建立索引,仅索引前10个字符。在表定义列last_name中定义为VARCHAR(16),这意味着每个条目我们已经保存了6个字节(如果姓氏中有UTF8字符,则保存更多字节)。在这个表中有1637个不同的值乘以6个字节大约9KB,并想象如果我们的表包含数百万行,这个数字将如何增长。

您可以在我的帖子Prefixed indexes in MySQL中阅读其他计算 N 数量的方法。

答案 4 :(得分:6)

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

注意 767 是在处理blob /文本索引时MySQL将索引列的字符数限制

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

答案 5 :(得分:3)

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

相反,将一个代理int键作为主键(使用auto_increment),并将loong键作为辅助UNIQUE。

答案 6 :(得分:2)

另一个处理此问题的好方法是创建没有唯一约束的TEXT字段,并添加一个唯一的兄弟VARCHAR字段,该字段包含TEXT字段的摘要(MD5,SHA1等)。当您插入或更新TEXT字段时,在整个TEXT字段上计算并存储摘要,然后您对可以快速搜索的整个TEXT字段(而不是某些前导部分)具有唯一性约束。

答案 7 :(得分:1)

添加另一个varChar(255)列(默认为空字符串not null)以在255个字符不足时保持溢出,并将此PK更改为使用两个列。这听起来并不像一个设计良好的数据库模式,我建议让数据建模人员查看你所拥有的内容,然后重构它以进行更多规范化。

答案 8 :(得分:0)

如果您的数据类型是 TEXT - 您必须将其更改为 VARCHAR
方案一:查询

ALTER TABLE table_name MODIFY COLUMN col_name datatype;
ALTER TABLE my_table MODIFY COLUMN my_col VARCHAR(255);

解决方案 2:GUI(MySQL 工作台)
step1 - 在文本框中写入 enter image description here

step2 - 编辑数据类型,应用 enter image description here

答案 9 :(得分:0)

删除该表,然后再次运行Spring Project。 这可能会有所帮助。 有时您要覆盖foreignKey。

答案 10 :(得分:0)

我知道已经很晚了,但是移除 Unique Key约束解决了该问题。我没有将TEXTLONGTEXT列用作PK,但是我试图使其成为唯一。我得到了1170 error,但是当我删除UK时,错误也被删除了。

我不完全理解为什么。

答案 11 :(得分:0)

向带有文本类型列的表添加索引时出现此错误。您需要声明每种文本类型要使用的大小量。

将尺寸大小放入括号()

如果使用了太多字节,则可以在括号中为varchar声明一个大小,以减少用于索引的数量。即使您为已经像varchar(1000)这样的类型声明了大小,也是如此。您无需像其他人一样创建新表。

添加索引

alter table test add index index_name(col1(255),col2(255));

添加唯一索引

alter table test add unique index_name(col1(255),col2(255));

答案 12 :(得分:0)

到目前为止,没有人提到它... utf8mb4是4字节,还可以存储图释(我们永远不应该再使用3字节utf8),并且我们可以避免出现类似Incorrect string value: \xF0\x9F\x98\...的错误,我们不应该使用典型的 VARCHAR(255),而不是 VARCHAR(191),因为在utf8mb4和VARCHAR(255)的情况下,同一部分数据存储在页面外,因此您无法为列创建索引VARCHAR(255),但可以使用VARCHAR(191)。这是因为ROW_FORMAT = COMPACT或ROW_FORMAT = REDUNDANT的最大索引列大小为767个字节。

对于较新的行格式ROW_FORMAT = DYNAMIC或ROW_FORMAT = COMPRESSED(需要较新的文件格式innodb_file_format = Barracuda而不是较旧的Antelope),最大索引列大小为3072。当innodb_large_prefix = 1(禁用)时,MySQL> = 5.6.3可用。默认情况下,对于MySQL <= 5.7.6,默认情况下启用,对于MySQL> = 5.7.7)。因此,在这种情况下,我们可以将VARCHAR(768)用于utf8mb4(或将VARCHAR(1024)用于旧的utf8)用于索引列。从5.7.7版本开始不推荐使用innodb_large_prefix选项,因为它的行为是内置的MySQL 8(在此版本中,该选项已删除)。

答案 13 :(得分:0)

转到mysql edit table->将列类型更改为varchar(45)

答案 14 :(得分:0)

您必须将列类型更改为varcharinteger才能建立索引。

答案 15 :(得分:0)

像这样使用

@Id
@Column(name = "userEmailId", length=100)
private String userEmailId;

答案 16 :(得分:0)

问题的解决方案是,在CREATE TABLE语句中,您可以在列创建定义后添加约束UNIQUE ( problemtextfield(300) ),以指定key300个字符的长度例如,对于TEXT字段。然后,300 problemtextfield字段的第一个TEXT字符必须是唯一的,之后的任何差异都将被忽略。

答案 17 :(得分:0)

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