ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

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

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

A
Arghavan

发生错误是因为 MySQL 只能索引 BLOB 或 TEXT 列的前 N 个字符。所以该错误主要发生在存在 TEXT 或 BLOB 的字段/列类型或属于 TEXTBLOB 类型的字段/列类型,例如 TINYBLOBMEDIUMBLOBLONGBLOBTINYTEXT、{ 9} 和 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 的值。a 的有效最大长度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,但是当我删除它时,错误就被删除了。
好像最后的链接坏了
A
Alexis Wilke

您应该定义要索引的 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 个字节。 {1 } 和 COMPACT 是给出此答案时唯一可用的格式。
M
Mike Evans

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

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

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

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)

如您所见,姓巴巴是最常见的。现在我们要找出最常出现的 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 数量的方法。


这还不够更新。我发现它比接受的答案更容易理解
s
stealth

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

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

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

我相信最简单的答案,它立即对我有用。谢谢。
谢谢!比其他答案好多了。
A
Abhishek Goel
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


p
par

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


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

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

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


A
Asish

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

我不完全明白为什么。


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

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


W
Who Dunnit

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


P
Pang

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


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

到目前为止没有人提到它......使用 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(在此版本中已删除选项)。


V
Vishal Singh

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

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

https://i.stack.imgur.com/8QfIn.png

https://i.stack.imgur.com/ocMMk.png


被否决的其他答案(我使用的)清楚地表明可以在不损害原始表的数据结构的情况下实现结果(VARCHAR 可能会截断,这就是使用 TEXT 的原因)。
m
milad bahari javan

我曾经使用过你提到的同样的错误:

CREATE INDEX idx_col1 ON my_table (col1);

然后我用这个替换它并解决了:

CREATE INDEX idx_col1 ON my_table (col1(255));

S
Stephen Kennedy

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


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

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


U
Ujash Patel

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


K
Krishna Das

像这样使用

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

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