# 1071 - La clave especificada era demasiado larga; la longitud máxima de la clave es 1000 bytes

102

Sé que ya se han respondido preguntas con este título, pero sigue leyendo. He leído detenidamente todas las demás preguntas / respuestas sobre este error antes de publicarlo.

Recibo el error anterior para la siguiente consulta:

CREATE TABLE IF NOT EXISTS `pds_core_menu_items` (
  `menu_id` varchar(32) NOT NULL,
  `parent_menu_id` int(32) unsigned DEFAULT NULL,
  `menu_name` varchar(255) DEFAULT NULL,
  `menu_link` varchar(255) DEFAULT NULL,
  `plugin` varchar(255) DEFAULT NULL,
  `menu_type` int(1) DEFAULT NULL,
  `extend` varchar(255) DEFAULT NULL,
  `new_window` int(1) DEFAULT NULL,
  `rank` int(100) DEFAULT NULL,
  `hide` int(1) DEFAULT NULL,
  `template_id` int(32) unsigned DEFAULT NULL,
  `alias` varchar(255) DEFAULT NULL,
  `layout` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`menu_id`),
  KEY `index` (`parent_menu_id`,`menu_link`,`plugin`,`alias`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

¿Alguien tiene idea de por qué y cómo solucionarlo? El problema es que esta misma consulta funciona perfectamente en mi máquina local y también funcionó en mi host anterior. Por cierto, es de un proyecto maduro, phpdevshell, así que supongo que estos tipos saben lo que están haciendo, aunque nunca se sabe.

Se agradece cualquier pista.

Estoy usando phpMyAdmin.

CodeVirtuoso
fuente

Respuestas:

170

Como dice @Devart, la longitud total de su índice es demasiado larga.

La respuesta corta es que no debería indexar columnas VARCHAR tan largas de todos modos, porque el índice será muy voluminoso e ineficiente.

La mejor práctica es usar índices de prefijo para que solo indexe una subcadena izquierda de los datos. De todos modos, la mayoría de sus datos serán mucho más cortos que 255 caracteres.

Puede declarar una longitud de prefijo por columna a medida que define el índice. Por ejemplo:

...
KEY `index` (`parent_menu_id`,`menu_link`(50),`plugin`(50),`alias`(50))
...

Pero, ¿cuál es la mejor longitud de prefijo para una columna determinada? Aquí hay un método para averiguarlo:

SELECT
 ROUND(SUM(LENGTH(`menu_link`)<10)*100/COUNT(`menu_link`),2) AS pct_length_10,
 ROUND(SUM(LENGTH(`menu_link`)<20)*100/COUNT(`menu_link`),2) AS pct_length_20,
 ROUND(SUM(LENGTH(`menu_link`)<50)*100/COUNT(`menu_link`),2) AS pct_length_50,
 ROUND(SUM(LENGTH(`menu_link`)<100)*100/COUNT(`menu_link`),2) AS pct_length_100
FROM `pds_core_menu_items`;

Le indica la proporción de filas que no tienen más de una longitud de cadena determinada en la menu_linkcolumna. Es posible que vea un resultado como este:

+---------------+---------------+---------------+----------------+
| pct_length_10 | pct_length_20 | pct_length_50 | pct_length_100 |
+---------------+---------------+---------------+----------------+
|         21.78 |         80.20 |        100.00 |         100.00 |
+---------------+---------------+---------------+----------------+

Esto le indica que el 80% de sus cadenas tienen menos de 20 caracteres y que todas sus cadenas tienen menos de 50 caracteres. Por lo tanto, no es necesario indexar más de una longitud de prefijo de 50, y ciertamente no es necesario indexar la longitud completa de 255 caracteres.

PD: Los tipos de datos INT(1)y INT(32)indican otro malentendido sobre MySQL. El argumento numérico no tiene ningún efecto relacionado con el almacenamiento o el rango de valores permitidos para la columna. INTes siempre de 4 bytes y siempre permite valores de -2147483648 a 2147483647. El argumento numérico trata sobre los valores de relleno durante la visualización, lo que no tiene ningún efecto a menos que use la ZEROFILLopción.

Bill Karwin
fuente
17
Muchas gracias por la explicación detallada. Además de solucionar un problema, también he aprendido algo valioso.
CodeVirtuoso
Realmente, una consulta muy útil para averiguar la longitud a la que se debe establecer el índice. He estado usando esto algunas veces para determinar la mejor longitud para un índice. ¡Gracias por compartir!
Niraj Kumar
1
Hay un error sutil en su práctica consulta para medir la longitud real de las cadenas: está asumiendo que todas las cadenas están presentes; es decir, que están todos ahí. Si algunos son nulos, perderá sus cálculos y no informará las cadenas cortas. Quiere usar count ([field_name]) en lugar de count (*).
D Mac
No utilizará más que el primer índice de "prefijo".
Rick James
28

Este error significa que la longitud del índice indexes superior a 1000 bytes. MySQL y los motores de almacenamiento pueden tener esta restricción. Tengo un error similar en MySQL 5.5 - 'La clave especificada era demasiado larga; la longitud máxima de la clave es 3072 bytes 'cuando se ejecutó este script:

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

UTF8 tiene varios bytes y la longitud de la clave se calcula de esta manera: 500 * 3 * 6 = 9000 bytes.

Pero tenga en cuenta que la siguiente consulta funciona.

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

... porque usé CHARSET = latin1, en este caso, la longitud de la clave es 500 * 6 = 3000 bytes.

Devart
fuente
7
Gracias por la respuesta, esto funciona, pero a costa de renunciar al juego de caracteres utf8. ¿Se puede superar esta restricción de alguna manera (tengo acceso completo al servidor), existe por una buena razón?
CodeVirtuoso
4
Este es un consejo terrible, aprenda qué son los juegos de caracteres y cómo esto causará problemas importantes en el futuro. Una base de datos de conjuntos de caracteres mixtos no solo causa problemas al usar uniones o sub-selecciones, sino que coloca sus datos en un formato no normalizado y puede ser casi imposible de corregir más adelante.
Geoffrey
16

Tuve este problema y lo resolví siguiendo:

Porque

Hay un error conocido con MySQL relacionado con MyISAM, el conjunto de caracteres UTF8 y los índices que puede consultar aquí.

Resolución

  • Asegúrese de que MySQL esté configurado con el motor de almacenamiento InnoDB.

  • Cambie el motor de almacenamiento utilizado de forma predeterminada para que las tablas nuevas siempre se creen correctamente:

    set GLOBAL storage_engine='InnoDb';

  • Para MySQL 5.6 y posterior, use lo siguiente:

    SET GLOBAL default_storage_engine = 'InnoDB';

  • Y finalmente asegúrese de seguir las instrucciones proporcionadas en Migración a MySQL .

Referencia

Vishrant
fuente
En la mayoría de los casos, nos olvidamos de configurar el motor de almacenamiento como 'InnoDB'. La siguiente respuesta es la más simple y probablemente resolverá el problema para la mayoría de los usuarios aquí. Gracias.
Frederiko Cesar
10

ejecute esta consulta antes de crear o modificar la tabla.

SET @@global.innodb_large_prefix = 1;

esto establecerá la longitud máxima de la clave en 3072 bytes

Raza Ahmed
fuente
3

Este límite de tamaño de índice parece ser mayor en compilaciones de 64 bits de MySQL.

Estaba alcanzando esta limitación tratando de volcar nuestra base de datos de desarrollo y cargarla en un VMWare virt local. Finalmente me di cuenta de que el servidor de desarrollo remoto era de 64 bits y había creado una virtud de 32 bits. Acabo de crear una virtud de 64 bits y pude cargar la base de datos localmente.

Ryan Olson
fuente
2

Acabo de pasar por alto este error simplemente cambiando los valores de la "longitud" en la base de datos original al total de alrededor de "1000" cambiando su estructura y luego exportando lo mismo al servidor. :)

Vishwadeep Kapoor
fuente
0

Estaba enfrentando el mismo problema, utilizado a continuación para resolverlo.

Mientras crea la base de datos, puede usar la codificación utf-8

p.ej. create database my_db character set utf8 collate utf8mb4;

EDITAR: (Teniendo en cuenta las sugerencias de los comentarios) Se cambió utf8_bin a utf8mb4

Sudhir Dhumal
fuente
2
Esto me indicó la dirección correcta. Para mí, tuve que cambiar mi colación a: utf8_general_ci
Ian Newland
2
Esta NO es la dirección correcta, ¡no hagas esto! MySQL utf8NO utf8lo es, es un formato propietario con errores que nunca debería haber llegado a buen término. utf8mb4es verdadero utf8y es el valor predeterminado recomendado para un utf8soporte adecuado .
Geoffrey
utf8mb4es la codificación correcta para usar en mysql y noutf8
alok
Fue solo un ejemplo; de todos modos, he actualizado la respuesta.
Sudhir Dhumal