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的字段/列类型或属于TEXT
或BLOB
类型如TINYBLOB
、MEDIUMBLOB
、LONGBLOB
、TINYTEXT
、@ 987654332@, 和LONGTEXT
,您尝试创建主键或索引。完整的BLOB
或TEXT
没有长度值,MySQL 无法保证列的唯一性,因为它是可变的和动态的大小。因此,当使用BLOB
或TEXT
类型作为索引时,必须提供 N 的值,以便 MySQL 可以确定键长度。但是,MySQL 不支持对 TEXT
或 BLOB
的密钥长度限制。 TEXT(88)
根本行不通。
当您尝试将表列从non-TEXT
和non-BLOB
类型如VARCHAR
和ENUM
转换为TEXT
或BLOB
类型时也会弹出错误,并且该列已经被定义为唯一约束或索引。 Alter Table SQL 命令将失败。
解决问题的方法是从索引或唯一约束中删除TEXT
或BLOB
列或将另一个字段设置为主键。如果您不能这样做,并且想要对TEXT
或BLOB
列设置限制,请尝试使用VARCHAR
类型并对其设置长度限制。默认情况下,VARCHAR
限制为最多 255 个字符,并且必须在声明后的括号内隐式指定其限制,即 VARCHAR(200)
将其限制为 200 个字符。
有时,即使您没有在表中使用TEXT
或BLOB
相关类型,也可能会出现错误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
,请计算SHA1
或MD5
哈希并将其用作PRIMARY KEY
。
【讨论】:
正是我想要的。谢谢! 大小(这里是 255)真的是字符而不是字节吗?因为我可以想象为 UTF-8 使用字符会非常复杂,而且没有额外的好处。 对不起,我没有听懂你的问题。来自文档:对于使用REDUNDANT
或 COMPACT
行格式的 InnoDB 表,索引键前缀长度限制为 767 字节。例如,您可能会在 TEXT
或 VARCHAR
列上超过 255 个字符的列前缀索引达到此限制,假设使用 utf8mb3 字符集并且每个字符最多 3 个字节。 @987654340 @ 和 COMPACT
是给出此答案时唯一可用的格式。【参考方案3】:
您可以在更改表请求中指定密钥长度,例如:
alter table authors ADD UNIQUE(name_first(20), name_second(20));
【讨论】:
这正是 我 需要解决同样的问题。谢谢! 你应该非常小心这种方法!这可能是最简单的解决方案,但在很多情况下不是最好的解决方案。您的键由两列组成,列顺序很重要。 最佳答案在这里。 这就是答案!【参考方案4】:MySQL 不允许索引 BLOB
、TEXT
和长 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
长度,例如例子。那么problemtextfield
TEXT
字段的第一个300
字符需要是唯一的,之后的任何差异都将被忽略。
【讨论】:
【参考方案11】:另外,如果你想在这个字段中使用索引,你应该使用 MyISAM 存储引擎和 FULLTEXT 索引类型。
【讨论】:
您可以考虑添加说明和文档链接。【参考方案12】:您必须将列类型更改为 varchar
或 integer
才能建立索引。
【讨论】:
您可以考虑添加说明和文档链接。【参考方案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
约束解决了这个问题。我没有使用 TEXT
或 LONGTEXT
列作为 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 字节