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

Posted

技术标签:

【中文标题】MySQL错误:没有密钥长度的密钥规范【英文标题】:MySQL error: key specification without a key length 【发布时间】:2010-12-22 01:47:09 【问题描述】:

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

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

我该如何解决这个问题?

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

【问题讨论】:

一张表不能有多个主键。你的意思是它有一个复合主键(包括多于一列)还是有多个 UNIQUE 键? 在我的情况下,出于某种原因,我有一个电子邮件列的 TEXT 类型而不是 VARCHAR。 对唯一的字母数字使用 VARCHAR。 【参考方案1】:

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

当您尝试将表列从non-TEXTnon-BLOB 类型如VARCHARENUM 转换为TEXTBLOB 类型时也会弹出错误,并且该列已经被定义为唯一约束或索引。 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

【讨论】:

dev.mysql.com/doc/refman/5.0/en/char.html "VARCHAR 列中的值是变长字符串。在 MySQL 5.0.3 之前,长度可以指定为 0 到 255 之间的值,在 5.0.3 和更高版本中可以指定为 0 到 65,535 之间的值MySQL 5.0.3 及更高版本中 VARCHAR 的有效最大长度取决于最大行大小(65,535 字节,所有列共享)" 你说“MySQL只能索引BLOB或TEXT列的前N个字符”,N的值是多少? "当您索引 BLOB 或 TEXT 列时,您必须指定索引的前缀长度。" dev.mysql.com/doc/refman/5.6/en/column-indexes.html 我知道已经很晚了,但是删除 Unique Key Constraint 解决了这个问题。我没有使用 TEXT 列作为 PK ,但我试图让它独一无二。我得到了1170 error,但是当我删除它时,错误就被删除了。 好像最后的链接坏了【参考方案2】:

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

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

这样可以正常工作:

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

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

如果您需要整个列成为PRIMARY KEY,请计算SHA1MD5 哈希并将其用作PRIMARY KEY

【讨论】:

正是我想要的。谢谢! 大小(这里是 255)真的是字符而不是字节吗?因为我可以想象为 UTF-8 使用字符会非常复杂,而且没有额外的好处。 对不起,我没有听懂你的问题。来自文档:对于使用 REDUNDANTCOMPACT 行格式的 InnoDB 表,索引键前缀长度限制为 767 字节。例如,您可能会在 TEXTVARCHAR 列上超过 255 个字符的列前缀索引达到此限制,假设使用 utf8mb3 字符集并且每个字符最多 3 个字节。 @987654340 @ 和 COMPACT 是给出此答案时唯一可用的格式。【参考方案3】:

您可以在更改表请求中指定密钥长度,例如:

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

【讨论】:

这正是 需要解决同样的问题。谢谢! 你应该非常小心这种方法!这可能是最简单的解决方案,但在很多情况下不是最好的解决方案。您的键由两列组成,列顺序很重要。 最佳答案在这里。 这就是答案!【参考方案4】:

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

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数量的方法。

【讨论】:

这还不够更新。我发现它比接受的答案很多更容易理解【参考方案5】:

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

将大小数量放在括号()内

如果使用了太多字节,您可以在 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));

【讨论】:

我相信的最简单的答案,它立即对我有用。谢谢。 谢谢!比其他答案好得多。【参考方案6】:
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

【讨论】:

【参考方案7】:

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

【讨论】:

您还应该非常小心完全“随机”的字符串,例如由 MD5()、SHA1() 或 UUID() 生成的字符串。您使用它们生成的每个新值都将以任意方式分布在大空间中,这会减慢 INSERT 和某些类型的 SELECT 查询: MD5、SHA1 在非恶意数据上的分布应该是统一的——这就是哈希的用途。 如果你能提供一些例子就太好了。【参考方案8】:

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

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

【讨论】:

【参考方案9】:

添加另一个 varChar(255) 列(默认为空字符串不为空)以在 255 个字符不够时保存溢出,并将此 PK 更改为使用这两个列。然而,这听起来不像是一个精心设计的数据库模式,我建议让数据建模师看看你有什么,以便重构它以实现更多规范化。

【讨论】:

【参考方案10】:

该问题的解决方案是,在您的CREATE TABLE 语句中,您可以在列创建定义之后添加约束UNIQUE ( problemtextfield(300) ),以指定300 字段的300 字符的key 长度,例如例子。那么problemtextfieldTEXT 字段的第一个300 字符需要是唯一的,之后的任何差异都将被忽略。

【讨论】:

【参考方案11】:

另外,如果你想在这个字段中使用索引,你应该使用 MyISAM 存储引擎和 FULLTEXT 索引类型。

【讨论】:

您可以考虑添加说明和文档链接。【参考方案12】:

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

【讨论】:

您可以考虑添加说明和文档链接。【参考方案13】:

到目前为止没有人提到它... 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。从 MySQL >= 5.6.3 开始可用,当 innodb_large_prefix=1(禁用默认情况下 MySQL = 5.7.7)。因此,在这种情况下,我们可以将 VARCHAR(768) 用于 utf8mb4(或 VARCHAR(1024) 用于旧 utf8)作为索引列。选项 innodb_large_prefix 自 5.7.7 起已弃用,因为它的行为是内置的 MySQL 8(在此版本中已删除选项)。

【讨论】:

【参考方案14】:

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

我不完全明白为什么。

【讨论】:

来自文档:对于 BLOB 和 TEXT 列上的索引,您必须指定索引前缀长度。对于 CHAR 和 VARCHAR,前缀长度是可选的。这是因为字段可能很长。与对这些列进行排序相同,您需要指定要在排序中考虑多少个字符。【参考方案15】:

如果您的数据类型是 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 - 在文本框中写入

step2 - 编辑数据类型,应用

【讨论】:

【参考方案16】:

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

【讨论】:

【参考方案17】:

删除该表并再次运行 Spring Project。 这可能会有所帮助。 有时你会覆盖foreignKey。

【讨论】:

【参考方案18】:

这样使用

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

【讨论】:

您可以考虑添加说明和文档链接。

以上是关于MySQL错误:没有密钥长度的密钥规范的主要内容,如果未能解决你的问题,请参考以下文章

错误:#1071 - 指定的密钥太长;最大密钥长度为 1000 字节 - mysql 5.0.91

指定的密钥太长;实体框架 6 中的最大密钥长度为 767 字节 Mysql 错误

Mysql Entity Framework 问题 - 指定的键太长;最大密钥长度为 3072 字节

BLOB / TEXT列'bestilling'用于密钥规范而没有密钥长度

如何规避 mySQL 中的最大密钥长度?

MySQL,错误 126:表的密钥文件不正确