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

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

我该如何解决这个问题?

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


The error happens because MySQL can index only the first N chars of a BLOB or TEXT column. So The error mainly happens when there is a field/column type of TEXT or BLOB or those belong to TEXT or BLOB types such as TINYBLOB, MEDIUMBLOB, LONGBLOB, TINYTEXT, MEDIUMTEXT, and LONGTEXT that you try to make a primary key or index. With full BLOB or TEXT without the length value, MySQL is unable to guarantee the uniqueness of the column as it’s of variable and dynamic size. So, when using BLOB or TEXT types as an index, the value of N must be supplied so that MySQL can determine the key length. However, MySQL doesn’t support a key length limit on TEXT or BLOB. TEXT(88) simply won’t work.

当您尝试将表列从非TEXT和非BLOB类型(如VARCHAR和ENUM)转换为TEXT或BLOB类型时,错误也会弹出,该列已经被定义为唯一约束或索引。Alter Table SQL命令将失败。

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

Sometimes, even though you don’t use TEXT or BLOB related type in your table, the Error 1170 may also appear. It happens in a situation such as when you specify VARCHAR column as primary key, but wrongly set its length or characters size. VARCHAR can only accepts up to 256 characters, so anything such as VARCHAR(512) will force MySQL to auto-convert the VARCHAR(512) to a SMALLTEXT datatype, which subsequently fails with error 1170 on key length if the column is used as primary key or unique or non-unique index. To solve this problem, specify a figure less than 256 as the size for VARCHAR field.

MySQL错误1170(42000):在键规范中使用的BLOB/TEXT列没有键长度


您应该定义要索引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个字节)

如果您需要整个列都是主键,计算SHA1或MD5哈希并将其用作主键。


添加另一个varChar(255)列(默认为空字符串而不是null),以在255个字符不够时保存溢出,并将此PK更改为使用两个列。然而,这听起来不像一个设计良好的数据库模式,我建议找一个数据建模师来看看你所拥有的,并对其进行重构以获得更多的规范化。


你可以在alter table请求中指定键的长度,就像这样:

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

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

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


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


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


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,这将导致最大的索引选择性。

有两种方法可以对数据库表进行计算。我将对此数据库转储进行演示。

假设我们想要将表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,想象一下,如果我们的表包含数百万行,这个数字将如何增长。

你可以在我的文章中阅读其他计算N的方法。


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


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


像这样使用

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

为了索引,必须将列类型更改为varchar或整型。


去mysql编辑表->更改列类型为varchar(45)。


Nobody mentioned it so far... with utf8mb4 which is 4-byte and can also store emoticons (we should never more use 3-byte utf8) and we can avoid errors like Incorrect string value: \xF0\x9F\x98\... we should not use typical VARCHAR(255) but rather VARCHAR(191) because in case utf8mb4 and VARCHAR(255) same part of data are stored off-page and you can not create index for column VARCHAR(255) but for VARCHAR(191) you can. It is because the maximum indexed column size is 767 bytes for ROW_FORMAT=COMPACT or ROW_FORMAT=REDUNDANT.

For newer row formats ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED (which requires newer file format innodb_file_format=Barracuda not older Antelope) maximum indexed column size is 3072. It is available since MySQL >= 5.6.3 when innodb_large_prefix=1 (disabled by default for MySQL <= 5.7.6 and enabled by default for MySQL >= 5.7.7). So in this case we can use VARCHAR(768) for utf8mb4 (or VARCHAR(1024) for old utf8) for indexed column. Option innodb_large_prefix is deprecated since 5.7.7 because its behavior is built-in MySQL 8 (in this version is option removed).


我得到这个错误时,添加一个索引的文本类型列表。您需要声明每个文本类型要使用的大小。

将大小放入括号()内

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

我知道现在已经很晚了,但是删除唯一键约束解决了这个问题。我没有使用TEXT或LONGTEXT列作为PK,但我试图使它独特。我得到了1170的错误,但当我删除了英国,错误也被删除了。

我不完全明白为什么。


DROP该表并再次运行Spring Project。 这可能会有所帮助。 有时你重写了foreignKey。


如果你的数据类型是TEXT -你将不得不改变它为VARCHAR 方案1:查询

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

解决方案2:GUI (MySQL工作台) 步骤1 -在文本框中写入

Step2 -编辑数据类型,应用


我曾经用过这个,也犯过你提到的错误:

CREATE INDEX idx_col1 ON my_table (col1);

然后我把它换成了这个,并解决了:

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