Error de MySQL: especificación de clave sin una longitud de clave

363

Tengo una tabla con una clave primaria que es un varchar (255). Han surgido algunos casos en los que 255 caracteres no son suficientes. Intenté cambiar el campo a un texto, pero aparece el siguiente error:

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

¿Cómo puedo arreglar esto?

editar: también debo señalar que esta tabla tiene una clave primaria compuesta con múltiples columnas.

GSto
fuente
99
Una tabla no puede tener varias claves primarias. ¿Quiere decir que tiene una clave primaria compuesta (que incluye más de una columna) o tiene varias UNIQUEclaves?
Quassnoi 01 de
1
En mi caso, por alguna razón, tenía un tipo de TEXTO para una columna de correo electrónico en lugar de VARCHAR.
Kris
Use VARCHAR para obtener caracteres alfanuméricos únicos.
JWC

Respuestas:

571

El error ocurre porque MySQL puede indexar solo los primeros N caracteres de un BLOB o TEXTcolumna. Por lo que el error se produce principalmente cuando hay un tipo de campo / columna de TEXTo BLOB o los que pertenecen a TEXT, o BLOBtipos, tales como TINYBLOB, MEDIUMBLOB, LONGBLOB, TINYTEXT, MEDIUMTEXT, y LONGTEXTque trate de hacer que una clave principal o un índice. Con el valor de longitud completo BLOBo TEXTsin él, MySQL no puede garantizar la unicidad de la columna, ya que tiene un tamaño variable y dinámico. Por lo tanto, cuando se utilizan BLOBo los TEXTtipos como índice, se debe proporcionar el valor de N para que MySQL pueda determinar la longitud de la clave. Sin embargo, MySQL no admite un límite de longitud de clave en TEXTo BLOB. TEXT(88)Simplemente no funcionará.

El error también aparecerá cuando intente convertir una columna de la tabla non-TEXTy non-BLOBescribir, como VARCHARy ENUMen TEXTo BLOBescribir, con la columna ya definida como restricciones o índice únicos. El comando Alter Table SQL fallará.

La solución al problema es eliminar la columna TEXTo BLOBdel índice o la restricción única o establecer otro campo como clave principal. Si no puede hacer eso y desea colocar un límite en la columna TEXTo BLOB, intente usar VARCHARtype y coloque un límite de longitud en él. Por defecto, VARCHARestá limitado a un máximo de 255 caracteres y su límite debe especificarse implícitamente dentro de un paréntesis justo después de su declaración, es decir, VARCHAR(200)lo limitará a 200 caracteres solamente.

A veces, a pesar de que no se utilicen TEXTo BLOBtipo relacionado en la tabla, el error 1170 puede también aparecer. Ocurre en una situación como cuando especifica una VARCHARcolumna como clave principal, pero establece incorrectamente su longitud o tamaño de caracteres. VARCHARsolo puede aceptar hasta 256 caracteres, por lo que cualquier cosa como VARCHAR(512)forzará a MySQL a convertir automáticamente VARCHAR(512)a un SMALLTEXTtipo de datos, que posteriormente falla con el error 1170 en la longitud de la clave si la columna se usa como clave primaria o índice único o no único. Para resolver este problema, especifique una cifra menor que 256 como el tamaño del VARCHARcampo.

Referencia: MySQL Error 1170 (42000): columna BLOB / TEXT utilizada en la especificación de clave sin una longitud de clave

Ponis OMG
fuente
13
dev.mysql.com/doc/refman/5.0/en/char.html "Los valores en las columnas VARCHAR son cadenas de longitud variable. La longitud se puede especificar como un valor de 0 a 255 antes de MySQL 5.0.3, y de 0 a 65.535 en 5.0.3 y versiones posteriores. La longitud máxima efectiva de un VARCHAR en MySQL 5.0.3 y posterior está sujeta al tamaño máximo de fila (65.535 bytes, que se comparte entre todas las columnas) "
umassthrower
1
Cuando dice "MySQL puede indexar solo los primeros N caracteres de una columna BLOB o TEXT", ¿cuál es el valor de N?
jameshfisher
2
"Cuando indexa una columna BLOB o TEXT, debe especificar una longitud de prefijo para el índice". dev.mysql.com/doc/refman/5.6/en/column-indexes.html
Vinicius Pinto
86

Debe definir qué parte inicial de una TEXTcolumna desea indexar.

InnoDBtiene una limitación de 768bytes por clave de índice y no podrá crear un índice por más tiempo.

Esto funcionará bien:

CREATE TABLE t_length (
      mydata TEXT NOT NULL,
      KEY ix_length_mydata (mydata(255)))
    ENGINE=InnoDB;

Tenga en cuenta que el valor máximo del tamaño de la clave depende del conjunto de caracteres de la columna. Son 767caracteres para un conjunto de caracteres de un solo byte LATIN1y solo 255caracteres para UTF8( MySQLsolo usos BMPque requieren como máximo 3bytes por carácter)

Si necesita que toda su columna sea PRIMARY KEY, calcule SHA1o MD5haga hash y úsela como a PRIMARY KEY.

Quassnoi
fuente
Exactamente lo que estaba buscando. ¡Gracias!
Jonathon Hill
¿El tamaño (255 aquí) está realmente en caracteres y no en bytes? Porque podría imaginar que usar caracteres para UTF-8 sería realmente complicado sin beneficios adicionales.
Alexis Wilke
Lo siento, no sigo tu pregunta. De los documentos: el límite de longitud del prefijo de clave de índice es de 767 bytes para las tablas InnoDB que utilizan el formato de fila REDUNDANTo COMPACT. Por ejemplo, puede alcanzar este límite con un índice de prefijo de columna de más de 255 caracteres en una columna TEXTo VARCHAR, suponiendo un conjunto de caracteres utf8mb3 y el máximo de 3 bytes para cada carácter. REDUNDANTy COMPACTeran los únicos formatos disponibles en el momento en que se dio esta respuesta.
Quassnoi
62

Puede especificar la longitud de la clave en la solicitud de alterar tabla, algo así como:

alter table authors ADD UNIQUE(name_first(20), name_second(20));
Mike Evans
fuente
1
Esto era exactamente lo que necesitaba para solucionar el mismo problema. ¡Gracias!
Por Quested Aronsson
2
¡Debes tener mucho cuidado con este enfoque! Esta es quizás la solución más fácil, pero en muchas situaciones no es la mejor. Su clave consta de dos columnas y el orden de las columnas es importante.
MrD
La mejor respuesta aquí.
George Chalhoub
Esto resuelve mi problema, muchas gracias!
dellair
22

Deshabilita MySQL indexación de un valor total de BLOB, TEXTy las largas VARCHARcolumnas porque los datos que contienen pueden ser enormes, e implícitamente índice DB serán grandes, es decir, ningún beneficio de índice.

MySQL requiere que defina los primeros N caracteres a indexar, y el truco consiste en elegir un número N que sea lo suficientemente largo como para proporcionar una buena selectividad, pero lo suficientemente corto como para ahorrar espacio. El prefijo debe ser lo suficientemente largo como para que el índice sea casi tan útil como lo sería si indexara toda la columna.

Antes de continuar, definamos algunos términos importantes. La selectividad del índice es la relación del total de valores indexados distintos y el número total de filas . Aquí hay un ejemplo para la tabla de prueba:

+-----+-----------+
| id  | value     |
+-----+-----------+
| 1   | abc       |
| 2   | abd       |
| 3   | adg       |
+-----+-----------+

Si indexamos solo el primer carácter (N = 1), la tabla de índice se verá como la siguiente tabla:

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| a             | 1,2,3     |
+---------------+-----------+

En este caso, la selectividad del índice es igual a IS = 1/3 = 0.33.

Veamos ahora qué sucederá si aumentamos el número de caracteres indexados a dos (N = 2).

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| ab             | 1,2      |
| ad             | 3        |
+---------------+-----------+

En este escenario IS = 2/3 = 0.66, lo que significa que aumentamos la selectividad del índice, pero también hemos aumentado el tamaño del índice. El truco consiste en encontrar el número mínimo N que dará como resultado una selectividad de índice máxima .

Hay dos enfoques que puede hacer cálculos para su tabla de base de datos. Haré una demostración en el volcado de esta base de datos .

Supongamos que queremos agregar la columna last_name en los empleados de la tabla al índice, y queremos definir el número N más pequeño que produzca la mejor selectividad del índice.

Primero identifiquemos los apellidos más frecuentes:

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)

Como puede ver, el apellido Baba es el más frecuente. Ahora vamos a encontrar los prefijos de apellido más frecuentes , comenzando con los prefijos de cinco letras.

+-----+--------+
| 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)

Hay muchos más casos de cada prefijo, lo que significa que tenemos que aumentar el número N hasta que los valores sean casi los mismos que en el ejemplo anterior.

Aquí están los resultados para 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    |
+-----+-----------+

Aquí están los resultados para 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)

Estos son muy buenos resultados. Esto significa que podemos hacer que el índice en la columna indexe last_namesolo los primeros 10 caracteres. En la tabla, la columna de definición last_namese define como VARCHAR(16), y esto significa que hemos guardado 6 bytes (o más si hay caracteres UTF8 en el apellido) por entrada. En esta tabla hay 1637 valores distintos multiplicados por 6 bytes que son aproximadamente 9 KB, e imagine cómo crecería este número si nuestra tabla contiene millones de filas.

Puede leer otras formas de calcular el número de N en mis índices indexados en MySQL .

MrD
fuente
3
Esto no se ha actualizado lo suficiente. Me pareció mucho más fácil de entender que la respuesta aceptada
Mawg dice que reinstalar a Monica el
10

Recibí este error al agregar un índice a una tabla con columnas de tipo de texto. Debe declarar la cantidad de tamaño que desea usar para cada tipo de texto.

Ponga la cantidad del tamaño dentro del paréntesis ()

Si se usan demasiados bytes, puede declarar un tamaño entre paréntesis para varchar para disminuir la cantidad utilizada para la indexación. Esto es incluso si declaró un tamaño para un tipo que ya es varchar (1000). No necesita crear una nueva tabla como han dicho otros.

Agregando índice

alter table test add index index_name(col1(255),col2(255));

Agregar índice único

alter table test add unique index_name(col1(255),col2(255));
sigilo
fuente
La respuesta más simple, creo, y me funcionó de inmediato. Gracias.
Matt Cremeens
4

Otra excelente manera de lidiar con esto es crear su campo TEXT sin la restricción única y agregar un campo hermano VARCHAR que sea único y contenga un resumen (MD5, SHA1, etc.) del campo TEXT. Calcule y almacene el resumen sobre todo el campo TEXTO cuando inserte o actualice el campo TEXTO, entonces tiene una restricción de unicidad sobre todo el campo TEXTO (en lugar de una parte inicial) que se puede buscar rápidamente.

par
fuente
1
También debe tener mucho cuidado con las cadenas completamente "aleatorias", como las producidas por MD5 (), SHA1 () o UUID (). Cada nuevo valor que genere con ellos se distribuirá de manera arbitraria en un gran espacio, lo que puede ralentizar INSERT y algunos tipos de consultas SELECT:
MrD
2
La distribución de MD5, SHA1 sobre datos no maliciosos debe ser uniforme, para eso están los hashes.
jb.
sería genial si pudieras dar algún ejemplo.
WebComer
3

No tiene valores largos como clave principal. Eso destruirá tu desempeño. Consulte el manual de mysql, sección 13.6.13 'Ajuste y resolución de problemas de InnoDB'.

En su lugar, tenga una clave int sustituta como primaria (con auto_increment) y su clave loong como UNIQUE secundaria.

Por Lindberg
fuente
2

Agregue otra columna varChar (255) (con el valor predeterminado como cadena vacía no nula) para contener el desbordamiento cuando 255 caracteres no son suficientes, y cambie esta PK para usar ambas columnas. Sin embargo, esto no parece un esquema de base de datos bien diseñado, y recomendaría obtener un modelador de datos para ver lo que tiene con el objetivo de refactorizarlo para una mayor Normalización.

Charles Bretana
fuente
2

La solución al problema es que en su CREATE TABLEdeclaración, puede agregar la restricción UNIQUE ( problemtextfield(300) )después de que la columna cree definiciones para especificar una keylongitud de 300caracteres para un TEXTcampo, por ejemplo. Entonces, los primeros 300caracteres del problemtextfield TEXTcampo tendrían que ser únicos y no se tendrían en cuenta las diferencias posteriores.

Quien Dunnit
fuente
1

Además, si desea usar el índice en este campo, debe usar el motor de almacenamiento MyISAM y el tipo de índice FULLTEXT.

Alexander Valinurov
fuente
Puede considerar agregar una explicación y un enlace a la documentación.
LeeGee
1

Nadie lo mencionó hasta ahora ... con utf8mb4, que es de 4 bytes y también puede almacenar emoticones (nunca más deberíamos usar utf8 de 3 bytes) y podemos evitar errores como Incorrect string value: \xF0\x9F\x98\...no deberíamos usar VARCHAR típico (255) sino VARCHAR ( 191) porque en caso de que utf8mb4 y VARCHAR (255) la misma parte de los datos se almacene fuera de la página y no puede crear el índice para la columna VARCHAR (255), pero sí para VARCHAR (191). Esto se debe a que el tamaño máximo de la columna indexada es de 767 bytes para ROW_FORMAT = COMPACT o ROW_FORMAT = REDUNDANT.

Para los formatos de fila más nuevos ROW_FORMAT = DYNAMIC o ROW_FORMAT = COMPRESSED (que requiere un formato de archivo más nuevo innodb_file_format = Barracuda no es un antílope más antiguo) el tamaño máximo de la columna indexada es 3072. Está disponible desde MySQL> = 5.6.3 cuando innodb_large_prefix = 1 (deshabilitado por defecto para MySQL <= 5.7.6 y habilitado por defecto para MySQL> = 5.7.7). Entonces, en este caso, podemos usar VARCHAR (768) para utf8mb4 (o VARCHAR (1024) para el antiguo utf8) para la columna indexada. La opción innodb_large_prefix está en desuso desde 5.7.7 porque su comportamiento está incorporado en MySQL 8 (en esta versión se elimina la opción).

mikep
fuente
0

Usted tiene que cambiar el tipo de columna para varcharo integerpara la indexación.

Manoj Mishra
fuente
Puede considerar agregar una explicación y un enlace a la documentación.
LeeGee
0

Vaya a mysql edit table-> cambie el tipo de columna a varchar(45).

Manoj Mishra
fuente
-1

Usar así

@Id
@Column(name = "userEmailId", length=100)
private String userEmailId;
Krishna Das
fuente
Puede considerar agregar una explicación y un enlace a la documentación.
LeeGee