Erreur MySQL: spécification de clé sans longueur de clé

363

J'ai une table avec une clé primaire qui est un varchar (255). Dans certains cas, 255 caractères ne suffisent pas. J'ai essayé de changer le champ en texte, mais j'obtiens l'erreur suivante:

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

Comment puis-je réparer cela?

edit: je dois également souligner que cette table a une clé primaire composite avec plusieurs colonnes.

GSto
la source
9
Une table ne peut pas avoir plusieurs clés primaires. Voulez-vous dire qu'il a une clé primaire composite (qui comprend plusieurs colonnes) ou qu'il a plusieurs UNIQUEclés?
Quassnoi
1
Dans mon cas, pour une raison quelconque, j'avais un type TEXT pour une colonne de courrier électronique au lieu de VARCHAR.
Kris
Utilisez VARCHAR pour des caractères alphanumériques uniques.
JWC mai

Réponses:

571

L'erreur se produit car MySQL ne peut indexer que les N premiers caractères d'un BLOB ou d'une TEXTcolonne. Donc , l'erreur se produit principalement quand il y a un type champ / colonne TEXTou blob ou appartiennent à ceux TEXTou BLOBtypes tels que TINYBLOB, MEDIUMBLOB, LONGBLOB, TINYTEXT, MEDIUMTEXTet LONGTEXTque vous essayez de faire une clé primaire ou un index. Avec pleine BLOBou TEXTsans valeur de longueur, MySQL n'est pas en mesure de garantir l'unicité de la colonne car elle est de taille variable et dynamique. Ainsi, lors de l'utilisation de BLOBou de TEXTtypes comme index, la valeur de N doit être fournie afin que MySQL puisse déterminer la longueur de clé. Cependant, MySQL ne prend pas en charge une limite de longueur de clé sur TEXTou BLOB.TEXT(88)ne fonctionnera tout simplement pas.

L'erreur s'affichera également lorsque vous tenterez de convertir une colonne de table à partir de non-TEXTet de non-BLOBtaper comme VARCHARet ENUMvers TEXTou de BLOBtype, la colonne étant déjà définie en tant que contraintes ou index uniques. La commande Alter Table SQL échouera.

La solution au problème consiste à supprimer la colonne TEXTou BLOBde l'index ou de la contrainte unique ou à définir un autre champ comme clé primaire. Si vous ne pouvez pas le faire et que vous souhaitez placer une limite sur la colonne TEXTou BLOB, essayez d'utiliser VARCHARtype et placez-y une limite de longueur. Par défaut, VARCHARest limité à un maximum de 255 caractères et sa limite doit être spécifiée implicitement dans une parenthèse juste après sa déclaration, c'est-à-dire VARCHAR(200)qu'il la limitera à 200 caractères seulement.

Parfois, même si vous ne l' utilisez TEXTou le BLOBtype connexes dans votre table, l'erreur 1170 peut également apparaître. Cela se produit dans une situation telle que lorsque vous spécifiez la VARCHARcolonne comme clé primaire, mais que vous définissez à tort sa longueur ou la taille des caractères. VARCHARne peut accepter que jusqu'à 256 caractères, donc tout ce VARCHAR(512)qui forcera MySQL à convertir automatiquement le VARCHAR(512)en un SMALLTEXTtype de données, qui échoue par la suite avec une erreur 1170 sur la longueur de la clé si la colonne est utilisée comme clé primaire ou index unique ou non unique. Pour résoudre ce problème, spécifiez un chiffre inférieur à 256 comme taille pourVARCHAR champ.

Référence: erreur MySQL 1170 (42000): colonne BLOB / TEXT utilisée dans la spécification de clé sans longueur de clé

Poneys OMG
la source
13
dev.mysql.com/doc/refman/5.0/en/char.html "Les valeurs dans les colonnes VARCHAR sont des chaînes de longueur variable. La longueur peut être spécifiée en tant que valeur de 0 à 255 avant MySQL 5.0.3 et de 0 à 65 535 dans la version 5.0.3 et les versions ultérieures. La longueur maximale effective d'un VARCHAR dans MySQL 5.0.3 et les versions ultérieures est soumise à la taille de ligne maximale (65 535 octets, qui est partagée entre toutes les colonnes) "
umassthrower
1
Lorsque vous dites "MySQL ne peut indexer que les N premiers caractères d'une colonne BLOB ou TEXT", quelle est la valeur de N?
jameshfisher
2
"Lorsque vous indexez une colonne BLOB ou TEXT, vous devez spécifier une longueur de préfixe pour l'index." dev.mysql.com/doc/refman/5.6/en/column-indexes.html
Vinicius Pinto
86

Vous devez définir la première partie d'une TEXTcolonne que vous souhaitez indexer.

InnoDBa une limitation d' 768octets par clé d'index et vous ne pourrez pas créer un index plus long que cela.

Cela fonctionnera bien:

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

Notez que la valeur maximale de la taille de clé dépend du jeu de caractères de la colonne. Ce sont des 767caractères pour un jeu de caractères à un octet comme LATIN1et uniquement des 255caractères pour UTF8( MySQLuniquement les utilisations BMPqui nécessitent au plus3 octets par caractère)

Si vous avez besoin que votre colonne entière soit PRIMARY KEY, calculez SHA1ouMD5 hachez et utilisez-la comme a PRIMARY KEY.

Quassnoi
la source
Exactement ce que je cherchais. Merci!
Jonathon Hill
La taille (255 ici) est-elle vraiment en caractères et non en octets? Parce que je pouvais imaginer que l'utilisation de caractères pour UTF-8 serait vraiment compliquée sans avantages supplémentaires.
Alexis Wilke
Désolé, je ne suis pas d'accord avec votre question. À partir des documents: La limite de longueur de préfixe de clé d'index est de 767 octets pour les tables InnoDB qui utilisent le format de ligne REDUNDANTou COMPACT. Par exemple, vous pouvez atteindre cette limite avec un index de préfixe de colonne de plus de 255 caractères sur une colonne TEXTor VARCHAR, en supposant un jeu de caractères utf8mb3 et un maximum de 3 octets pour chaque caractère. REDUNDANTet COMPACTétaient les seuls formats disponibles au moment où cette réponse a été donnée.
Quassnoi
62

Vous pouvez spécifier la longueur de clé dans la demande de modification de table, quelque chose comme:

alter table authors ADD UNIQUE(name_first(20), name_second(20));
Mike Evans
la source
1
C'était exactement ce dont j'avais besoin pour résoudre le même problème. Merci!
Par Quested Aronsson
2
Vous devez être très prudent avec cette approche! C'est peut-être la solution la plus simple, mais dans de nombreuses situations, ce n'est pas la meilleure. Votre clé est composée de deux colonnes et l'ordre des colonnes est important.
MrD
Meilleure réponse ici.
George Chalhoub
Cela résout mon problème, merci beaucoup!
dellair
22

L' indexation d' une valeur MySQL exclut du financement complet de BLOB, TEXTet de longues VARCHARcolonnes parce que les données qu'elles contiennent peuvent être énormes, et implicitement l' indice DB sera grand, ce qui signifie aucun avantage de l' indice.

MySQL nécessite que vous définissiez les N premiers caractères à indexer, et l'astuce consiste à choisir un nombre N suffisamment long pour donner une bonne sélectivité, mais suffisamment court pour économiser de l'espace. Le préfixe doit être suffisamment long pour rendre l'index presque aussi utile qu'il le serait si vous aviez indexé la colonne entière.

Avant d'aller plus loin, définissons quelques termes importants. La sélectivité d'index est le rapport du total des valeurs indexées distinctes et du nombre total de lignes . Voici un exemple de table de test:

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

Si nous indexons uniquement le premier caractère (N = 1), la table d'index ressemblera au tableau suivant:

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

Dans ce cas, la sélectivité de l'indice est égale à IS = 1/3 = 0,33.

Voyons maintenant ce qui se passera si nous augmentons le nombre de caractères indexés à deux (N = 2).

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

Dans ce scénario, IS = 2/3 = 0,66, ce qui signifie que nous avons augmenté la sélectivité de l'indice, mais nous avons également augmenté la taille de l'indice. L'astuce consiste à trouver le nombre minimal N qui se traduira par une sélectivité d'indice maximale .

Vous pouvez effectuer deux calculs pour votre table de base de données. Je ferai une démonstration sur le vidage de cette base de données .

Disons que nous voulons ajouter la colonne last_name dans les employés de la table à l'index, et nous voulons définir le plus petit nombre N qui produira la meilleure sélectivité d'index.

Identifions d'abord les noms de famille les plus fréquents:

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)

Comme vous pouvez le voir, le nom de famille Baba est le plus fréquent. Nous allons maintenant trouver les préfixes last_name les plus fréquents , en commençant par les préfixes à cinq lettres.

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

Il y a beaucoup plus d'occurrences de chaque préfixe, ce qui signifie que nous devons augmenter le nombre N jusqu'à ce que les valeurs soient presque les mêmes que dans l'exemple précédent.

Voici les résultats pour 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    |
+-----+-----------+

Voici les résultats pour 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)

Ce sont de très bons résultats. Cela signifie que nous pouvons faire l'index sur la colonne last_nameen n'indexant que les 10 premiers caractères. Dans la colonne de définition de table last_nameest définie comme VARCHAR(16), et cela signifie que nous avons enregistré 6 octets (ou plus s'il y a des caractères UTF8 dans le nom de famille) par entrée. Dans ce tableau, il y a 1637 valeurs distinctes multipliées par 6 octets soit environ 9 Ko, et imaginez comment ce nombre augmenterait si notre tableau contient des millions de lignes.

Vous pouvez lire d'autres façons de calculer le nombre de N dans mes post index préfixés dans MySQL .

MrD
la source
3
Cela n'a pas été suffisamment mis à jour. J'ai trouvé que c'était beaucoup plus facile à comprendre que la réponse acceptée
Mawg dit réintégrer Monica
10

J'ai eu cette erreur lors de l'ajout d'un index à une table avec des colonnes de type texte. Vous devez déclarer la taille que vous souhaitez utiliser pour chaque type de texte.

Mettez la taille entre les parenthèses ()

Si trop d'octets sont utilisés, vous pouvez déclarer une taille entre crochets pour varchar pour diminuer la quantité utilisée pour l'indexation. C'est même si vous avez déclaré une taille pour un type déjà comme varchar (1000). Vous n'avez pas besoin de créer une nouvelle table comme d'autres l'ont dit.

Ajout d'index

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

Ajout d'un index unique

alter table test add unique index_name(col1(255),col2(255));
furtif
la source
Réponse la plus simple, je crois, et cela a fonctionné pour moi tout de suite. Merci.
Matt Cremeens
4

Une autre excellente façon de gérer cela est de créer votre champ TEXT sans la contrainte unique et d'ajouter un champ VARCHAR frère unique qui contient un résumé (MD5, SHA1, etc.) du champ TEXT. Calculez et stockez le résumé sur l'intégralité du champ TEXT lorsque vous insérez ou mettez à jour le champ TEXT, puis vous avez une contrainte d'unicité sur l'ensemble du champ TEXT (plutôt que sur une partie principale) qui peut être recherchée rapidement.

par
la source
1
Vous devez également être très prudent avec les chaînes complètement «aléatoires», telles que celles produites par MD5 (), SHA1 () ou UUID (). Chaque nouvelle valeur que vous générez avec eux sera distribuée de manière arbitraire sur un grand espace, ce qui peut ralentir INSERT et certains types de requêtes SELECT:
MrD
2
La distribution de MD5, SHA1 sur des données non malveillantes doit être uniforme - c'est à cela que servent les hachages.
jb.
ce serait formidable si vous pouviez donner un exemple.
WebComer
3

N'ont pas de valeurs longues comme clé primaire. Cela détruira vos performances. Voir le manuel mysql, section 13.6.13 «Réglage et dépannage des performances d'InnoDB».

Au lieu de cela, ayez une clé int de substitution comme primaire (avec auto_increment), et votre clé loong comme UNIQUE secondaire.

Per Lindberg
la source
2

Ajoutez une autre colonne varChar (255) (avec par défaut une chaîne vide non nulle) pour maintenir le débordement lorsque 255 caractères ne sont pas suffisants, et modifiez ce PK pour utiliser les deux colonnes. Cependant, cela ne ressemble pas à un schéma de base de données bien conçu, et je recommanderais à un modélisateur de données de regarder ce que vous avez en vue de le refactoriser pour plus de normalisation.

Charles Bretana
la source
2

La solution au problème est que dans votre CREATE TABLEinstruction, vous pouvez ajouter la contrainte UNIQUE ( problemtextfield(300) )après la colonne créer des définitions pour spécifier une keylongueur de 300caractères pour un TEXTchamp, par exemple. Ensuite, les premiers 300caractères du problemtextfield TEXTchamp devraient être uniques, et toute différence après cela serait ignorée.

Qui Dunnit
la source
1

En outre, si vous souhaitez utiliser l'index dans ce champ, vous devez utiliser le moteur de stockage MyISAM et le type d'index FULLTEXT.

Alexander Valinurov
la source
Vous pourriez envisager d'ajouter une explication et un lien vers la documentation.
LeeGee
1

Personne ne l'a mentionné jusqu'à présent ... avec utf8mb4 qui est de 4 octets et peut également stocker des émoticônes (nous ne devrions plus utiliser utf8 de 3 octets) et nous pouvons éviter les erreurs comme Incorrect string value: \xF0\x9F\x98\...nous ne devrions pas utiliser VARCHAR typique (255) mais plutôt VARCHAR ( 191) car dans le cas où utf8mb4 et VARCHAR (255), la même partie des données est stockée hors page et vous ne pouvez pas créer d'index pour la colonne VARCHAR (255) mais pour VARCHAR (191) vous pouvez. C'est parce que la taille de colonne indexée maximale est de 767 octets pour ROW_FORMAT = COMPACT ou ROW_FORMAT = REDUNDANT.

Pour les nouveaux formats de ligne ROW_FORMAT = DYNAMIC ou ROW_FORMAT = COMPRESSED (qui nécessite un format de fichier plus récent innodb_file_format = Barracuda antilope pas plus ancien), la taille de colonne indexée maximale est de 3072. Elle est disponible depuis MySQL> = 5.6.3 lorsque innodb_large_prefix = 1 (désactivé par défaut pour MySQL <= 5.7.6 et activé par défaut pour MySQL> = 5.7.7). Donc, dans ce cas, nous pouvons utiliser VARCHAR (768) pour utf8mb4 (ou VARCHAR (1024) pour l'ancien utf8) pour la colonne indexée. L'option innodb_large_prefix est déconseillée depuis 5.7.7 car son comportement est intégré à MySQL 8 (dans cette version, l'option est supprimée).

mikep
la source
0

Vous devez changer le type de colonne en varcharou integerpour l'indexation.

Manoj Mishra
la source
Vous pourriez envisager d'ajouter une explication et un lien vers la documentation.
LeeGee
0

Allez dans mysql edit table-> changez le type de colonne en varchar(45).

Manoj Mishra
la source
-1

Utilisez comme ça

@Id
@Column(name = "userEmailId", length=100)
private String userEmailId;
Krishna Das
la source
Vous pourriez envisager d'ajouter une explication et un lien vers la documentation.
LeeGee