# 1071 - La clé spécifiée était trop longue. la longueur maximale de la clé est de 767 octets

StackOverflow https://stackoverflow.com/questions/1814532

Question

Lorsque j'ai exécuté la commande suivante:

ALTER TABLE `mytable` ADD UNIQUE (
`column1` ,
`column2`
);

J'ai reçu ce message d'erreur:

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

Informations sur la colonne 1 et la colonne 2:

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci

Je pense que varchar(20) ne nécessite que 21 octets tandis que varchar(500) ne requiert que 501 octets. Donc, le nombre total d'octets est 522, moins de 767. Alors, pourquoi ai-je reçu le message d'erreur?

<*>
Était-ce utile?

La solution

767 octets correspond à la limitation du préfixe déclaré pour Tables InnoDB dans MySQL version 5.6 (et versions antérieures). La longueur est de 1 000 octets pour les tables MyISAM. À partir de la version 5.7 de MySQL, cette limite a été augmentée à 3072 octets.

Vous devez également savoir que si vous définissez un index sur un grand champ char ou varchar codé en utf8mb4, vous devez diviser la longueur maximale de préfixe d'index de 767 octets (ou 3072 octets) par 4, ce qui donne 191. En effet, la longueur maximale d'un caractère utf8mb4 est de quatre octets. Pour un caractère utf8, il s'agirait de trois octets, ce qui donnerait une longueur maximale de préfixe d'index de 254.

Une option consiste à placer une limite inférieure sur vos champs VARCHAR.

Une autre option (selon la réponse à ce problème ) est d'obtenir le sous-ensemble de la colonne plutôt que le montant total, à savoir:

ALTER TABLE `mytable` ADD UNIQUE ( column1(15), column2(200) );

Faites des ajustements car vous avez besoin de la clé à appliquer, mais je me demande si cela vaudrait la peine d'examiner votre modèle de données concernant cette entité pour voir s'il y a des améliorations qui vous permettraient de mettre en œuvre les règles métier souhaitées sans toucher à MySQL. limitation.

Autres conseils

Si quelqu'un a des problèmes avec INNODB / Utf-8 pour essayer de placer un index UNIQUE sur un champ VARCHAR(256), réglez-le sur VARCHAR(255). Il semble que 255 soit la limite.

Lorsque vous atteignez la limite. Définissez les éléments suivants.

  • INNODB utf8 VARCHAR(255)
  • INNODB utf8mb4 VARCHAR(191)

MySQL suppose dans le pire des cas le nombre d'octets par caractère dans la chaîne. Pour l'encodage MySQL 'utf8', il s'agit de 3 octets par caractère, car cet encodage n'autorise pas les caractères supérieurs à U+FFFF. Pour l'encodage MySQL 'utf8mb4', il s'agit de 4 octets par caractère, puisque c'est ce que MySQL appelle l'UTF-8.

En supposant que vous utilisiez 'utf8', votre première colonne contiendra 60 octets de l'index, et votre seconde 1500 autres.

exécuter cette requête avant votre requête:

SET @@global.innodb_large_prefix = 1;

cela augmentera la limite à 3072 bytes.

Quel encodage de caractères utilisez-vous? Certains jeux de caractères (comme UTF-16, etc.) utilisent plusieurs octets par caractère.

Solution pour le framework Laravel

Conformément à la documentation Laravel 5.4. * ; Vous devez définir la longueur de chaîne par défaut dans la boot méthode du fichier app/Providers/AppServiceProvider.php comme suit:

use Illuminate\Support\Facades\Schema;

public function boot() 
{
    Schema::defaultStringLength(191); 
}

Explication de ce correctif, donnée par Laravel 5.4. * documentation :

  

Laravel utilise le jeu de caractères utf8mb4 par défaut, qui inclut la prise en charge du stockage de & "emojis &"; dans la base de données. Si vous utilisez une version de MySQL antérieure à la version 5.7.7 ou MariaDB antérieure à la version 10.2.2, vous devrez peut-être configurer manuellement la longueur de chaîne par défaut générée par les migrations afin que MySQL puisse en créer un index. Vous pouvez le configurer en appelant la méthode Schema::defaultStringLength dans votre AppServiceProvider.

     

Vous pouvez également activer l'option innodb_large_prefix pour votre   base de données. Reportez-vous à la documentation de votre base de données pour savoir comment   comment activer correctement cette option.

  

Je pense que varchar (20) ne nécessite que 21 octets tandis que varchar (500) seulement   nécessite 501 octets. Donc, le nombre total d'octets est 522, moins de 767. Alors pourquoi   ai-je reçu le message d'erreur?

UTF8 nécessite 3 octets par caractère pour stocker la chaîne. Dans votre cas, par conséquent, 20 + 500 caractères = 20 * 3 + 500 * 3 = 1560 octets, soit < strong> plus que autorisé 767 octets.

La limite pour UTF8 est de 767/3 = 255 caractères , pour UTF8mb4 qui utilise 4 octets par caractère, elle est de 767/4 = 191 caractères.

Il existe deux solutions à ce problème si vous devez utiliser une colonne plus longue que la limite:

  1. Utilisez & "moins cher &"; codage (celui qui nécessite moins d'octets par caractère)
    Dans mon cas, j’avais besoin d’ajouter un index unique sur une colonne contenant la chaîne d’article SEO, car j’utilise uniquement [A-z0-9\-] des caractères pour le référencement, j’ai utilisé latin1_general_ci qui n’utilise qu’un octet par caractère. La colonne peut donc avoir une longueur de 767 octets.
  2. Créez un hachage à partir de votre colonne et utilisez un index unique uniquement sur cette

    L'autre option pour moi était de créer une autre colonne qui stockerait le hachage de SEO, cette colonne aurait UNIQUE clé pour garantir que les valeurs de SEO sont uniques. J'ajouterais également KEY l'index à la colonne SEO d'origine pour accélérer la recherche.

De nombreux utilisateurs ont déjà répondu à la question de savoir pourquoi vous obtenez un message d'erreur. Ma réponse concerne la façon de le réparer et de l'utiliser tel quel.

Voir ce lien .

  1. Ouvrez le client MySQL (ou le client MariaDB). C'est un outil en ligne de commande.
  2. Il vous demandera votre mot de passe, entrez votre mot de passe correct.
  3. Sélectionnez votre base de données à l'aide de cette commande use my_database_name;
  

La base de données a été modifiée

  1. set global innodb_large_prefix=on;
  

Requête OK, 0 ligne affectée (0,00 s)

  1. set global innodb_file_format=Barracuda;
  

Requête OK, 0 ligne affectée (0.02 sec)

  1. Allez dans votre base de données sur phpMyAdmin ou quelque chose comme ça pour une gestion facile. > Sélectionnez la base de données & Gt; Afficher la table structure & Gt; Accédez à l'onglet Opérations . > Remplacez ROW_FORMAT par DYNAMIC et enregistrez les modifications.
  2. Accédez à l'onglet Structure de la table > Cliquez sur le bouton Unique .
  3. Fait. Maintenant, il ne devrait pas y avoir d'erreur.

Le problème de ce correctif est que vous exportez la base de données vers un autre serveur (par exemple de localhost vers un hôte réel) et que vous ne pouvez pas utiliser la ligne de commande MySQL sur ce serveur. Vous ne pouvez pas le faire fonctionner là-bas.

Specified key was too long; max key length is 767 bytes

Vous avez reçu ce message car 1 octet est égal à 1 caractère uniquement si vous utilisez le jeu de caractères latin-1. Si vous utilisez utf8, chaque caractère sera considéré comme 3 octets lors de la définition de votre colonne de clé. Si vous utilisez utf8mb4, chaque caractère sera considéré comme 4 octets lors de la définition de votre colonne de clé. Ainsi, vous devez multiplier la limite de caractères du champ de votre clé par, 1, 3 ou 4 (dans mon exemple) pour déterminer le nombre d'octets que le champ de clé tente d'autoriser. Si vous utilisez uft8mb4, vous ne pouvez définir que 191 caractères pour un champ natif, InnoDB, clé primaire. Il suffit de ne pas dépasser 767 octets.

vous pouvez ajouter une colonne du md5 des colonnes longues

Nous avons rencontré ce problème lorsque nous avons essayé d'ajouter un index UNIQUE à un champ VARCHAR (255) à l'aide de utf8mb4. Bien que le problème soit déjà bien décrit, je souhaitais ajouter quelques conseils pratiques sur la manière de résoudre ce problème.

Lors de l’utilisation de utf8mb4, les caractères comptent pour 4 octets, alors que sous utf8, ils pourraient correspondre à 3 octets. Les bases de données InnoDB ont pour limite que les index ne peuvent contenir que 767 octets. Ainsi, lorsque vous utilisez utf8, vous pouvez stocker 255 caractères (767/3 = 255), mais avec utf8mb4, vous ne pouvez enregistrer que 191 caractères (767/4 = 191).

Vous pouvez absolument ajouter des index réguliers pour VARCHAR(255) les champs à l'aide de utf8mb4, mais il se trouve que la taille de l'index est automatiquement tronquée à 191 caractères - comme unique_key ici:

.

 Capture d'écran de Sequel Pro montrant un index tronqué à 191 caractères

Cela convient, car les index classiques sont simplement utilisés pour aider MySQL à parcourir vos données plus rapidement. Le champ entier n'a pas besoin d'être indexé.

Alors, pourquoi MySQL tronque-t-il automatiquement l'index pour les index normaux, mais génère une erreur explicite lorsque vous essayez de le faire pour des index uniques? Eh bien, pour que MySQL puisse déterminer si la valeur insérée ou mise à jour existe déjà, il doit en fait indexer la valeur entière et pas seulement une partie de celle-ci.

À la fin de la journée, si vous souhaitez avoir un index unique sur un champ, le contenu complet du champ doit tenir dans l’index. Pour utf8mb4, cela signifie que vous réduisez la longueur de votre champ VARCHAR à 191 caractères ou moins. Si vous n'avez pas besoin de utf8mb4 pour cette table ou ce champ, vous pouvez le remettre à utf8 et pouvoir conserver vos champs de 255 longueurs.

Voici ma réponse initiale:

  

Je viens de déposer la base de données et de le recréer comme ceci, et l'erreur a disparu:

     

drop database if exists rhodes; create database rhodes default CHARACTER set utf8 default COLLATE utf8_general_ci;

Cependant, cela ne fonctionne pas dans tous les cas.

Il s'agit en fait d'un problème d'utilisation d'index sur les colonnes VARCHAR avec le jeu de caractères utf8 (ou utf8mb4), avec les colonnes VARCHAR comportant plus d'une certaine longueur de caractères. Dans le cas de <=>, cette certaine longueur est 191.

Reportez-vous à la section Index long de cet article pour plus d'informations sur l'utilisation des index longs dans la base de données MySQL: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database- jeu de caractères à utf8mb4

J'ai effectué des recherches sur ce sujet et obtenu un changement personnalisé

Pour MySQL Workbench 6.3.7 Version, une phase graphique est disponible

  1. Démarrez Workbench et sélectionnez la connexion.
  2. Accédez à la gestion ou à l'instance et sélectionnez Fichier d'options.
  3. Si Workbench vous demande la permission de lire le fichier de configuration, puis autorisez-le en appuyant deux fois sur OK.
  4. Au centre, la fenêtre du fichier d'options de l'administrateur s'affiche.
  5. Allez sur l'onglet InnoDB et vérifiez le préfixe innodb_large_ s'il n'a pas été coché dans la section Général.
  6. définissez la valeur de l'option innodb_default_row_format sur DYNAMIC.

Pour les versions inférieures à 6.3.7, les options directes ne sont pas disponibles. Vous devez donc vous connecter avec une invite de commande

.
  1. Démarrer CMD en tant qu'administrateur.
  2. Aller au directeur où le serveur mysql est installé La plupart des cas, c'est à " C: \ Program Files \ MySQL \ Serveur MySQL 5.7 \ bin " alors le commandement est " cd \ " " cd Program Files \ MySQL \ Serveur MySQL 5.7 \ bin ".
  3. Commande maintenant lancée mysql -u nom d'utilisateur -p databasecheema Maintenant, il a demandé le mot de passe de l'utilisateur respectif. Fournissez le mot de passe et entrez dans l'invite mysql.
  4. Nous devons définir certains paramètres globaux et entrer les commandes ci-dessous une à une. set global innodb_large_prefix = on; set global innodb_file_format = barracuda; set global innodb_file_per_table = true;
  5. À la dernière modification, nous devons modifier le ROW_FORMAT de la table requise. Par défaut, son COMPACT doit être défini sur DYNAMIC.
  6. utilise la commande suivante alter table nom_table ROW_FORMAT = DYNAMIC;
  7. terminé

J'ai résolu ce problème avec:

varchar(200) 

remplacé par

varchar(191)

tous les varchars qui ont plus de 200 remplacent par 191 ou les mettent en texte.

5 solutions de contournement:

La limite a été augmentée à 5.7.7 (MariaDB 10.2.2?). Et il peut être augmenté avec un peu de travail en 5.6 (10.1).

Si vous atteignez la limite en raison d'une tentative d'utilisation de CHARACTER SET utf8mb4. Ensuite, effectuez l’une des opérations suivantes (chacune présente un inconvénient) pour éviter l’erreur:

⚈  Upgrade to 5.7.7 for 3072 byte limit -- your cloud may not provide this;
⚈  Change 255 to 191 on the VARCHAR -- you lose any values longer than 191 characters (unlikely?);
⚈  ALTER .. CONVERT TO utf8 -- you lose Emoji and some of Chinese;
⚈  Use a "prefix" index -- you lose some of the performance benefits.
⚈  Or... Stay with older version but perform 4 steps to raise the limit to 3072 bytes:

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=1;
SET GLOBAL innodb_large_prefix=1;
logout & login (to get the global values);
ALTER TABLE tbl ROW_FORMAT=DYNAMIC;  -- (or COMPRESSED)

- http://mysql.rjweb.org/doc.php/limits# 767_limit_in_innodb_indexes

changez votre classement. Vous pouvez utiliser utf8_general_ci , qui prend en charge la quasi-totalité des

.

Changer simplement de utf8mb4 à utf8 lors de la création de tables a résolu mon problème. Par exemple: CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; à CREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;.

Pour laravel 5,7 ou 5,6 ou 5,8

Étapes à suivre

  1. Accédez à App\Providers\AppServiceProvider.php.
  2. Ajoutez ceci au fournisseur use Illuminate\Support\Facades\Schema; en haut.
  3. Dans la fonction de démarrage Ajouter ceci Schema::defaultStringLength(191);

que tous, profitez-en.

Sur la base de la colonne indiquée ci-dessous, ces 2 colonnes de chaîne de variable utilisent utf8_general_ci un classement (utf8 jeu de caractères est impliqué).

Dans MySQL, utf8mb4 jeu de caractères utilise au maximum 3 octets pour chaque caractère. Ainsi, 500 * 3 = 1500 octets devraient être alloués, ce qui est bien supérieur aux 767 octets autorisés par MySQL. C’est pourquoi vous obtenez cette erreur 1071.

En d'autres termes, vous devez calculer le nombre de caractères en fonction de la représentation d'octet du jeu de caractères, car chaque jeu de caractères n'est pas une représentation à un seul octet (comme vous l'aviez supposé). I.E. <=> MySQL utilise au maximum 3 octets par caractère, 767/3 & # 8776; 255 caractères, et pour <=> une représentation maximale de 4 octets, 767/4 & # 8776; 191 caractères.

On sait aussi que MySQL

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci

J'ai trouvé cette requête utile pour détecter les colonnes dont l'index ne respectait pas la longueur maximale:

SELECT
  c.TABLE_NAME As TableName,
  c.COLUMN_NAME AS ColumnName,
  c.DATA_TYPE AS DataType,
  c.CHARACTER_MAXIMUM_LENGTH AS ColumnLength,
  s.INDEX_NAME AS IndexName
FROM information_schema.COLUMNS AS c
INNER JOIN information_schema.statistics AS s
  ON s.table_name = c.TABLE_NAME
 AND s.COLUMN_NAME = c.COLUMN_NAME 
WHERE c.TABLE_SCHEMA = DATABASE()
  AND c.CHARACTER_MAXIMUM_LENGTH > 191 
  AND c.DATA_TYPE IN ('char', 'varchar', 'text')

Veuillez vérifier si sql_mode est comme

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

si c'est le cas, remplacez par

sql_mode=NO_ENGINE_SUBSTITUTION

OU

redémarrez votre serveur en changeant votre fichier my.cnf (en mettant les suivants)

innodb_large_prefix=on

Dans mon cas, j’ai eu ce problème lors de la sauvegarde d’une base de données à l’aide des caractères de sortie / entrée de redirection Linux. Par conséquent, je change la syntaxe comme décrit ci-dessous. PS: utilisant un terminal Linux ou Mac.

Sauvegarde (sans la > redirection)

# mysqldump -u root -p databasename -r bkp.sql

Restaurer (sans la < redirection)

# mysql -u root -p --default-character-set=utf8 databasename
mysql> SET names 'utf8'
mysql> SOURCE bkp.sql

L'erreur & "La clé spécifiée était trop longue; La longueur maximale de la clé est de 767 octets " simple disparu.

Longueur d'index & amp; MySQL / MariaDB

Laravel utilise le caractère utf8mb4 défini par défaut, qui inclut la prise en charge du stockage de & "emojis &"; dans la base de données. Si vous utilisez une version de MySQL antérieure à la version 5.7.7 ou MariaDB antérieure à la version 10.2.2, vous devrez peut-être configurer manuellement la longueur de chaîne par défaut générée par les migrations afin que MySQL puisse en créer un index. Vous pouvez le configurer en appelant la méthode Schema :: defaultStringLength dans votre AppServiceProvider:

.

use Illuminate\Support\Facades\Schema;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Schema::defaultStringLength(191);
}

Vous pouvez également activer l'option innodb_large_prefix pour votre base de données. Reportez-vous à la documentation de votre base de données pour savoir comment activer correctement cette option.

Référence de la documentation officielle de laravel: https://laravel.com/ docs / 5.7 / migrations

Modifiez CHARSET du champ d'index se plaignant en " latin1 "

ALTER TABLE tbl MODIFIER myfield myfield varchar (600) JEU DE CARACTERES latin1 DEFAULT NULL;
latin1 prend un octet pour un caractère au lieu de quatre

Si vous créez quelque chose comme:

CREATE TABLE IF NOT EXISTS your_table (
  id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
  name varchar(256) COLLATE utf8mb4_bin NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY name (name)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

cela devrait être quelque chose comme

CREATE TABLE IF NOT EXISTS your_table (
      id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(256) COLLATE utf8mb4_bin NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

mais vous devez vérifier le caractère unique de cette colonne par rapport au code ou ajouter une nouvelle colonne en tant que MD5 ou SHA1 de la colonne varchar

En raison des limitations de préfixes, cette erreur se produira. 767 octets est la limite de préfixe indiquée pour les tables InnoDB dans les versions de MySQL antérieures à 5.7. La longueur est de 1 000 octets pour les tables MyISAM. À partir de la version 5.7 de MySQL, cette limite a été augmentée à 3072 octets.

L'exécution de ce qui suit sur le service vous générant l'erreur devrait résoudre votre problème. Ceci doit être exécuté dans la CLI de MYSQL.

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=on;
SET GLOBAL innodb_large_prefix=on;

Pour résoudre ce problème, cela fonctionne pour moi comme un charme.

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

Si vous avez changé innodb_log_file_size récemment, essayez de restaurer la valeur précédente qui fonctionnait.

Pour moi, le problème de " # 1071 - La clé spécifiée était trop long; La longueur maximale de la clé est de 767 octets " résolus après modification de la combinaison primarykey / uniquekey en limitant la taille de la colonne à 200.

ALTER TABLE `mytable` ADD UNIQUE (
`column1` (200) ,
`column2` (200)
);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top