我有一个主键是 varchar(255) 的表。在某些情况下,255 个字符是不够的。我尝试将字段更改为文本,但出现以下错误:
BLOB/TEXT column 'message_id' used in key specification without a key length
我怎样才能解决这个问题?
编辑:我还应该指出这个表有一个多列的复合主键。
我有一个主键是 varchar(255) 的表。在某些情况下,255 个字符是不够的。我尝试将字段更改为文本,但出现以下错误:
BLOB/TEXT column 'message_id' used in key specification without a key length
我怎样才能解决这个问题?
编辑:我还应该指出这个表有一个多列的复合主键。
发生错误是因为 MySQL 只能索引 BLOB 或TEXT
列的前 N 个字符。因此,该错误主要发生在存在字段/列类型TEXT
或 BLOB 或属于TEXT
或BLOB
诸如TINYBLOB
、MEDIUMBLOB
、LONGBLOB
、TINYTEXT
、等类型的字段/列类型MEDIUMTEXT
,并且LONGTEXT
您尝试创建主键或索引时。BLOB
无论长度值是否完整TEXT
,MySQL 都无法保证列的唯一性,因为它具有可变和动态大小。因此,当使用BLOB
orTEXT
类型作为索引时,必须提供 N 的值,以便 MySQL 可以确定键长度。TEXT
但是,MySQL 不支持或的密钥长度限制BLOB
。TEXT(88)
根本行不通。
non-TEXT
当您尝试将表列从和non-BLOB
类型转换为VARCHAR
和ENUM
转换为TEXT
或类型时,也会弹出该错误BLOB
,该列已定义为唯一约束或索引。Alter Table SQL 命令将失败。
该问题的解决方案是从索引或唯一约束中删除TEXT
或BLOB
列或将另一个字段设置为主键。如果您不能这样做,并且想要对TEXT
orBLOB
列设置限制,请尝试使用VARCHAR
type 并对其设置长度限制。默认情况下,VARCHAR
限制为最多 255 个字符,并且必须在声明后的括号内隐式指定其限制,即VARCHAR(200)
仅将其限制为 200 个字符。
有时,即使您没有在表中使用TEXT
或BLOB
相关类型,也可能会出现错误 1170。它发生在您将VARCHAR
列指定为主键但错误地设置其长度或字符大小的情况下。VARCHAR
最多只能接受 256 个字符,因此任何诸如VARCHAR(512)
将强制 MySQL 自动转换VARCHAR(512)
为SMALLTEXT
数据类型的内容,如果该列用作主键或唯一或非唯一索引,则随后会在键长度上失败并出现错误 1170。要解决此问题,请将小于 256 的数字指定为VARCHAR
字段的大小。
参考:MySQL 错误 1170 (42000): BLOB/TEXT Column Used in Key Specification without a Key Length
TEXT
您应该定义要索引的列的前导部分。
InnoDB
每个索引键的字节数有限制,768
您将无法创建比此更长的索引。
这将正常工作:
CREATE TABLE t_length (
mydata TEXT NOT NULL,
KEY ix_length_mydata (mydata(255)))
ENGINE=InnoDB;
请注意,键大小的最大值取决于列字符集。它是767
用于单字节字符集的字符,LATIN1
并且仅255
用于UTF8
(MySQL
仅使用每个字符BMP
最多需要字节的字符)3
如果您需要将整个列作为PRIMARY KEY
,请计算SHA1
或MD5
散列并将其用作PRIMARY KEY
.
您可以在更改表请求中指定密钥长度,例如:
alter table authors ADD UNIQUE(name_first(20), name_second(20));
BLOB
MySQL 不允许索引,TEXT
和 long列的完整值,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数的方法MySQL 中的前缀索引。
向具有文本类型列的表添加索引时出现此错误。您需要为每种文本类型声明要使用的大小。
将大小数量放在括号 ( ) 内
如果使用了太多字节,您可以在 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));
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
另一个很好的处理方法是创建没有唯一约束的 TEXT 字段,并添加一个唯一的兄弟 VARCHAR 字段,该字段包含 TEXT 字段的摘要(MD5、SHA1 等)。当您插入或更新 TEXT 字段时,计算并存储整个 TEXT 字段的摘要,然后您可以快速搜索整个 TEXT 字段(而不是某个前导部分)的唯一性约束。
不要将长值作为主键。那会破坏你的表现。请参阅 mysql 手册,第 13.6.13 节“InnoDB 性能调整和故障排除”。
相反,将代理 int 键作为主键(使用 auto_increment),并将您的 loong 键作为辅助 UNIQUE。
添加另一个 varChar(255) 列(默认为空字符串不为空)以在 255 个字符不够时保持溢出,并将此 PK 更改为使用两列。然而,这听起来不像是一个设计良好的数据库模式,我建议让数据建模师看看你有什么,以便重构它以实现更多规范化。
该问题的解决方案是,在您的语句中,您可以在列创建定义之后CREATE TABLE
添加约束,以指定字段的字符长度,例如。那么该字段的第一个字符需要是唯一的,之后的任何差异都将被忽略。UNIQUE ( problemtextfield(300) )
key
300
TEXT
300
problemtextfield
TEXT
另外,如果要在该字段中使用索引,则应使用 MyISAM 存储引擎和 FULLTEXT 索引类型。
您必须将列类型更改为varchar
或integer
用于索引。
到目前为止没有人提到它......使用 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(在此版本中已删除选项)。
我知道已经很晚了,但是删除约束Unique Key
解决了这个问题。我没有将TEXT
orLONGTEXT
列用作 PK ,但我试图使其独一无二。我得到了1170 error
,但是当我删除时UK
,错误也被删除了。
我不完全明白为什么。
转到 mysql edit table
-> 将列类型更改为varchar(45)
.
删除该表并再次运行 Spring Project。这可能会有所帮助。有时您会覆盖foreignKey。
像这样使用
@Id
@Column(name = "userEmailId", length=100)
private String userEmailId;