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

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

我该如何解决这个问题?

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


当前回答

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

CREATE INDEX idx_col1 ON my_table (col1);

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

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

其他回答

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

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

像这样使用

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

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

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