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

103

Je sais que les questions portant ce titre ont déjà reçu une réponse, mais continuez à lire. J'ai lu attentivement toutes les autres questions / réponses sur cette erreur avant de poster.

J'obtiens l'erreur ci-dessus pour la requête suivante:

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;

Quelqu'un a-t-il une idée pourquoi et comment y remédier? Le hic est que cette même requête fonctionne parfaitement sur ma machine locale, et a également fonctionné sur mon hôte précédent. Btw.it vient d'un projet mature - phpdevshell - donc je suppose que ces gars savent ce qu'ils font, bien que vous ne le sachiez jamais.

Tout indice apprécié.

J'utilise phpMyAdmin.

CodeVirtuoso
la source

Réponses:

171

Comme le dit @Devart, la longueur totale de votre index est trop longue.

La réponse courte est que vous ne devriez de toute façon pas indexer des colonnes VARCHAR aussi longues, car l'index sera très volumineux et inefficace.

La meilleure pratique consiste à utiliser des index de préfixe afin d'indexer uniquement une sous-chaîne gauche des données. La plupart de vos données seront de toute façon beaucoup plus courtes que 255 caractères.

Vous pouvez déclarer une longueur de préfixe par colonne lorsque vous définissez l'index. Par exemple:

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

Mais quelle est la meilleure longueur de préfixe pour une colonne donnée? Voici une méthode pour le découvrir:

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`;

Il vous indique la proportion de lignes qui n'ont pas plus d'une longueur de chaîne donnée dans la menu_linkcolonne. Vous pourriez voir une sortie comme celle-ci:

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

Cela vous indique que 80% de vos chaînes contiennent moins de 20 caractères et que toutes vos chaînes contiennent moins de 50 caractères. Il n'est donc pas nécessaire d'indexer plus d'une longueur de préfixe de 50, et certainement pas besoin d'indexer la longueur totale de 255 caractères.

PS: Les types de données INT(1)et INT(32)indiquent un autre malentendu sur MySQL. L'argument numérique n'a aucun effet lié au stockage ou à la plage de valeurs autorisées pour la colonne. INTest toujours de 4 octets et autorise toujours les valeurs comprises entre -2147483648 et 2147483647. L'argument numérique concerne les valeurs de remplissage pendant l'affichage, ce qui n'a aucun effet sauf si vous utilisez l' ZEROFILLoption.

Bill Karwin
la source
17
Merci beaucoup pour l'explication détaillée. En plus de résoudre un problème, j'ai également appris quelque chose de précieux.
CodeVirtuoso
Vraiment, requête très utile pour savoir la longueur à laquelle l'index doit être défini. Je l'ai utilisé à plusieurs reprises pour déterminer la meilleure longueur d'un index. Merci pour le partage!
Niraj Kumar le
1
Il y a un bogue subtil dans votre requête pratique pour mesurer la longueur réelle des chaînes: vous supposez que les chaînes sont toutes présentes; c'est-à-dire qu'ils sont tous là. Si certains sont nuls, cela annulera vos calculs et sous-déclarera les chaînes courtes. Vous souhaitez utiliser count ([field_name]) au lieu de count (*).
D Mac
Il n'utilisera pas plus que le premier index "préfixe".
Rick James
28

Cette erreur signifie que la longueur de l'index indexest supérieure à 1000 octets. MySQL et les moteurs de stockage peuvent avoir cette restriction. J'ai eu une erreur similaire sur MySQL 5.5 - «La clé spécifiée était trop longue; la longueur maximale de la clé est de 3072 octets lors de l'exécution de ce 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 est multi-octets, et la longueur de clé est calculée de cette manière - 500 * 3 * 6 = 9000 octets.

Mais notez que la prochaine requête fonctionne!

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;

... parce que j'ai utilisé CHARSET = latin1, dans ce cas, la longueur de la clé est de 500 * 6 = 3000 octets.

Devart
la source
7
Merci pour la réponse, cela fonctionne, mais au prix d'abandonner le jeu de caractères utf8. Cette restriction peut-elle être surmontée d'une manière ou d'une autre (j'ai un accès complet au serveur), est-ce là pour une bonne raison?
CodeVirtuoso
4
C'est un conseil terrible, veuillez savoir ce que sont les jeux de caractères et comment cela causera des problèmes majeurs à l'avenir. Une base de données de jeux de caractères mixtes pose non seulement des problèmes lors de l'utilisation de jointures ou de sous-sélections, mais elle place vos données dans un format non normalisé et peut être quasiment impossible à corriger plus tard.
Geoffrey
17

J'ai eu ce problème et je l'ai résolu en suivant:

Cause

Il existe un bogue connu avec MySQL lié à MyISAM, le jeu de caractères UTF8 et les index que vous pouvez vérifier ici.

Résolution

  • Assurez-vous que MySQL est configuré avec le moteur de stockage InnoDB.

  • Modifiez le moteur de stockage utilisé par défaut afin que les nouvelles tables soient toujours créées de manière appropriée:

    set GLOBAL storage_engine='InnoDb';

  • Pour MySQL 5.6 et versions ultérieures, utilisez ce qui suit:

    SET GLOBAL default_storage_engine = 'InnoDB';

  • Et enfin, assurez-vous que vous suivez les instructions fournies dans Migrer vers MySQL .

Référence

Vishrant
la source
Dans la plupart des cas, nous oublions de configurer le moteur de stockage comme «InnoDB». La réponse suivante est la plus simple et résoudra probablement le problème pour la plupart des utilisateurs ici. Merci.
Frederiko Cesar
10

exécutez cette requête avant de créer ou de modifier la table.

SET @@global.innodb_large_prefix = 1;

cela définira la longueur maximale de la clé à 3072 octets

Raza Ahmed
la source
3

Cette limite de taille d'index semble être plus grande sur les versions 64 bits de MySQL.

J'atteignais cette limitation en essayant de vider notre base de données de développement et de la charger sur un VMWare local virt. Finalement, j'ai réalisé que le serveur de développement distant était 64 bits et j'avais créé un virt 32 bits. Je viens de créer un virt 64 bits et j'ai pu charger la base de données localement.

Ryan Olson
la source
2

Je viens de contourner cette erreur en changeant simplement les valeurs de la "longueur" dans la base de données d'origine au total d'environ "1000" en modifiant sa structure, puis en exportant la même chose vers le serveur. :)

Vishwadeep Kapoor
la source
0

J'étais confronté au même problème, utilisé ci-dessous la requête pour le résoudre.

Lors de la création de DB, vous pouvez utiliser l'encodage utf-8

par exemple. create database my_db character set utf8 collate utf8mb4;

EDIT: (Compte tenu des suggestions des commentaires) Changement de utf8_bin en utf8mb4

Sudhir Dhumal
la source
2
Cela m'a orienté dans la bonne direction. Pour moi, j'ai dû changer ma collation en: utf8_general_ci
Ian Newland
2
Ce n'est PAS la bonne direction, ne faites pas cela! MySQL utf8n'est PAS utf8, c'est un format propriétaire bogué qui n'aurait jamais dû se concrétiser. utf8mb4est vrai utf8et est la valeur par défaut recommandée pour une utf8prise en charge appropriée .
Geoffrey
utf8mb4est le bon encodage à utiliser sur mysql et nonutf8
alok
C'était juste un exemple - de toute façon, j'ai mis à jour la réponse.
Sudhir Dhumal