SQL密钥长度错误没有意义

时间:2012-03-07 15:26:03

标签: mysql

以下内容对我来说非常合理:

CREATE TABLE `mydb`.`Temp` (
 `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
 `x` VARCHAR ( 300 ) NOT NULL ,
 `id_foo` INT NULL DEFAULT NULL,
 FOREIGN KEY ( `id_foo`) REFERENCES `Foo` (`id`) ON DELETE CASCADE ,
 INDEX (`id_foo`),
 INDEX (`x`),
 UNIQUE (`id_foo`, `x`)
) ENGINE = INNODB;

使用MySQL会出现错误

#1071 - Specified key was too long; max key length is 767 bytes

这似乎是错误的,因为整行是309字节:小于767,甚至不是一半。发生了什么事?

4 个答案:

答案 0 :(得分:6)

根据MYSQL Documantation:http://dev.mysql.com/doc/refman/5.0/en/create-index.html

MySQL对用于在列上定义索引的空间量有不同的限制

  • 对于MyISAM,它是1,000字节;
  • 对于InnoDB来说是767。

此外,这些列的数据类型很重要 - 对于VARCHAR,它是3x

因此,VARCHAR(300)上的索引就像在表中一样,将占用900个大于767字节的字节,即最大密钥长度。

编辑:显然这是不是 MySQL的错误,但MySQL中的 UTF8 最多支持3个字节。此外,引入4字节utf8字符集(WL#1213)最大可能密钥长度从255更改为191 字符(191 * 4 + 2 = 766,其中2个字节保持长度)。从MySQL 5.5或更高版本开始,所有-utf,-utf8mb4,-utf16,-utf32都会受此影响。

答案 1 :(得分:2)

尝试确定该指数需要多长时间才能保持有效:

SELECT COUNT(DISTINCT(`x`)) as n_unique,
COUNT(DISTINCT(LEFT(`x`,200))) as n_100,
COUNT(DISTINCT(LEFT(`x`,150))) as n_150,
COUNT(DISTINCT(LEFT(`x`,100))) as n_100,
COUNT(DISTINCT(LEFT(`x`,50))) as n_50,
COUNT(DISTINCT(LEFT(`x`,25))) as n_25,
COUNT(DISTINCT(LEFT(`x`,10))) as n_10
FROM Temp;

将每个n_结果除以n_unique将为您提供百分比覆盖率。一旦你有了这个,你就可以用较少数量的角色获得不错的覆盖率。

ALTER TABLE Temp ADD index x_improved(20)

其中20实际上是上面给出的不同变量的n_个数。

答案 2 :(得分:0)

这是你的INDEX (x)问题。如果VARCHAR更短,它就可以工作。

答案 3 :(得分:0)

您使用的字符集是什么?

基于该错误,您似乎使用的是多字节字符集,可能是utf8,每个字符保留3个字节。因此,utf8中的varchar(300)会产生900字节的密钥长度,超过了innodb的767限制。

要使用这些索引创建表,您需要使用不同的字符集,或缩短列的长度。如果您只是索引x,我建议只索引该列的前255个字符,但给定您的唯一索引包含x该解决方案不可行,因为如果它们匹配前255个字符,它会拒绝重复值,即使它们在最后45个字符中有所不同。

以下是一些可行的示例:

-- shorten x to 255 characters
CREATE TABLE `mydb`.`Temp` (
 `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
 `x` VARCHAR ( 255 ) NOT NULL ,
 `id_foo` INT NULL DEFAULT NULL,
 FOREIGN KEY ( `id_foo`) REFERENCES `Foo` (`id`) ON DELETE CASCADE ,
 INDEX (`id_foo`),
 INDEX (`x`),
 UNIQUE (`id_foo`, `x`)
) ENGINE = INNODB;

-- use single-byte character set
CREATE TABLE `mydb`.`Temp` (
 `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
 `x` VARCHAR ( 300 ) NOT NULL ,
 `id_foo` INT NULL DEFAULT NULL,
 FOREIGN KEY ( `id_foo`) REFERENCES `Foo` (`id`) ON DELETE CASCADE ,
 INDEX (`id_foo`),
 INDEX (`x`),
 UNIQUE (`id_foo`, `x`)
) ENGINE = INNODB DEFAULT CHARSET LATIN1;