# 1071 - Der angegebene Schlüssel war zu lang. Die maximale Schlüssellänge beträgt 1000 Byte

102

Ich weiß, dass Fragen mit diesem Titel bereits beantwortet wurden, aber bitte lesen Sie weiter. Ich habe alle anderen Fragen / Antworten zu diesem Fehler vor dem Posten gründlich gelesen.

Ich erhalte den obigen Fehler für die folgende Abfrage:

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;

Hat jemand eine Idee warum und wie man es behebt? Der Haken ist - dieselbe Abfrage funktioniert perfekt auf meinem lokalen Computer und auch auf meinem vorherigen Host. Übrigens stammt es aus einem ausgereiften Projekt - phpdevshell - also würde ich vermuten, dass diese Jungs wissen, was sie tun, obwohl man es nie weiß.

Jeder Hinweis geschätzt.

Ich benutze phpMyAdmin.

CodeVirtuoso
quelle

Antworten:

170

Wie @Devart sagt, ist die Gesamtlänge Ihres Index zu lang.

Die kurze Antwort lautet, dass Sie ohnehin nicht so lange VARCHAR-Spalten indizieren sollten, da der Index sehr umfangreich und ineffizient sein wird.

Die beste Vorgehensweise ist die Verwendung von Präfixindizes damit Sie nur einen linken Teilstring der Daten . Die meisten Ihrer Daten sind ohnehin viel kürzer als 255 Zeichen.

Sie können eine Präfixlänge pro Spalte deklarieren, wenn Sie den Index definieren. Beispielsweise:

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

Aber was ist die beste Präfixlänge für eine bestimmte Spalte? Hier ist eine Methode, um dies herauszufinden:

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

Es gibt Ihnen den Anteil der Zeilen an, die nicht mehr als eine bestimmte Zeichenfolgenlänge in der menu_linkSpalte haben. Möglicherweise sehen Sie folgende Ausgabe:

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

Dies zeigt Ihnen, dass 80% Ihrer Zeichenfolgen weniger als 20 Zeichen und alle Ihre Zeichenfolgen weniger als 50 Zeichen enthalten. Es ist also nicht erforderlich, mehr als eine Präfixlänge von 50 zu indizieren, und es ist sicherlich nicht erforderlich, die gesamte Länge von 255 Zeichen zu indizieren.

PS: Der Datentyp INT(1)und INT(32)weist auf ein weiteres Missverständnis in Bezug auf MySQL hin. Das numerische Argument hat keine Auswirkung auf die Speicherung oder den für die Spalte zulässigen Wertebereich. INTbeträgt immer 4 Byte und erlaubt immer Werte von -2147483648 bis 2147483647. Das numerische Argument bezieht sich auf das Auffüllen von Werten während der Anzeige, was keine Auswirkung hat, wenn Sie die ZEROFILLOption nicht verwenden.

Bill Karwin
quelle
17
Vielen Dank für die ausführliche Erklärung. Ich habe nicht nur ein Problem behoben, sondern auch etwas Wertvolles gelernt.
CodeVirtuoso
Wirklich sehr nützliche Abfrage, um herauszufinden, auf welche Länge der Index gesetzt werden soll. Ich habe dies einige Male verwendet, um die beste Länge für einen Index zu bestimmen. Ich danke Ihnen für das Teilen!
Niraj Kumar
1
Ihre praktische Abfrage enthält einen subtilen Fehler, mit dem Sie messen können, wie lang die Zeichenfolgen wirklich sind: Sie gehen davon aus, dass alle Zeichenfolgen vorhanden sind. das heißt, dass sie alle da sind. Wenn einige null sind, werden Ihre Berechnungen verworfen und kurze Zeichenfolgen werden nicht gemeldet. Sie möchten count ([Feldname]) anstelle von count (*) verwenden.
D Mac
Es wird nicht mehr als der erste "Präfix" -Index verwendet.
Rick James
28

Dieser Fehler bedeutet, dass die Indexlänge indexmehr als 1000 Byte beträgt. MySQL und Speicher-Engines können diese Einschränkung haben. Ich habe einen ähnlichen Fehler in MySQL 5.5 erhalten - 'Der angegebene Schlüssel war zu lang. Die maximale Schlüssellänge beträgt 3072 Byte, wenn dieses Skript ausgeführt wird:

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 besteht aus mehreren Bytes, und die Schlüssellänge wird auf diese Weise berechnet - 500 * 3 * 6 = 9000 Bytes.

Beachten Sie jedoch, dass die nächste Abfrage funktioniert!

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;

... weil ich CHARSET = latin1 verwendet habe, beträgt die Schlüssellänge in diesem Fall 500 * 6 = 3000 Bytes.

Devart
quelle
7
Vielen Dank für die Antwort, dies funktioniert, aber auf Kosten des Verzichts auf den utf8-Zeichensatz. Kann diese Einschränkung irgendwie überwunden werden (ich habe vollen Serverzugriff), gibt es sie aus gutem Grund?
CodeVirtuoso
4
Dies ist ein schrecklicher Rat. Bitte erfahren Sie, was Zeichensätze sind und wie dies in Zukunft zu größeren Problemen führen wird. Eine gemischte Zeichensatzdatenbank verursacht nicht nur Probleme bei der Verwendung von Verknüpfungen oder Unterauswahlen, sondern versetzt Ihre Daten in ein nicht normalisiertes Format und kann später kaum korrigiert werden.
Geoffrey
16

Ich hatte dieses Problem und löste es wie folgt:

Ursache

Es gibt einen bekannten Fehler mit MySQL im Zusammenhang mit MyISAM, dem UTF8-Zeichensatz und den Indizes, die Sie hier überprüfen können.

Auflösung

  • Stellen Sie sicher, dass MySQL mit der InnoDB-Speicher-Engine konfiguriert ist.

  • Ändern Sie die standardmäßig verwendete Speicher-Engine so, dass immer neue Tabellen entsprechend erstellt werden:

    set GLOBAL storage_engine='InnoDb';

  • Verwenden Sie für MySQL 5.6 und höher Folgendes:

    SET GLOBAL default_storage_engine = 'InnoDB';

  • Stellen Sie schließlich sicher, dass Sie die Anweisungen unter Migrieren zu MySQL befolgen .

Referenz

Vishrant
quelle
In den meisten Fällen vergessen wir, die Speicher-Engine als 'InnoDB' zu konfigurieren. Die folgende Antwort ist die einfachste und wird wahrscheinlich das Problem für die meisten Benutzer hier lösen. Vielen Dank.
Frederiko Cesar
10

Führen Sie diese Abfrage aus, bevor Sie eine Tabelle erstellen oder ändern.

SET @@global.innodb_large_prefix = 1;

Dadurch wird die maximale Schlüssellänge auf 3072 Byte festgelegt

Raza Ahmed
quelle
3

Diese Indexgrößenbeschränkung scheint bei 64-Bit-Builds von MySQL größer zu sein.

Ich habe diese Einschränkung erreicht und versucht, unsere Entwicklungsdatenbank zu sichern und auf eine lokale VMWare-Virt zu laden. Schließlich wurde mir klar, dass der Remote-Dev-Server 64-Bit war und ich eine 32-Bit-Virt erstellt hatte. Ich habe gerade eine 64-Bit-Virt erstellt und konnte die Datenbank lokal laden.

Ryan Olson
quelle
2

Ich habe diesen Fehler soeben umgangen, indem ich die Werte der "Länge" in der Originaldatenbank auf insgesamt "1000" geändert habe, indem ich ihre Struktur geändert und diese dann auf den Server exportiert habe. :) :)

Vishwadeep Kapoor
quelle
0

Ich hatte das gleiche Problem, das in der folgenden Abfrage verwendet wurde, um es zu beheben.

Beim Erstellen der Datenbank können Sie die utf-8-Codierung verwenden

z.B. create database my_db character set utf8 collate utf8mb4;

BEARBEITEN: (unter Berücksichtigung von Vorschlägen aus Kommentaren) utf8_bin in utf8mb4 geändert

Sudhir Dhumal
quelle
2
Dies zeigte mir die richtige Richtung. Für mich musste ich meine Sortierung ändern in: utf8_general_ci
Ian Newland
2
Dies ist NICHT die richtige Richtung, tu das nicht! MySQL utf8ist NICHT utf8, es ist ein fehlerhaftes proprietäres Format, das niemals zum Tragen kommen sollte. utf8mb4ist wahr utf8und die empfohlene Standardeinstellung für eine ordnungsgemäße utf8Unterstützung.
Geoffrey
utf8mb4ist die korrekte Codierung auf MySQL zu verwenden und nichtutf8
alok
Es war nur ein Beispiel - trotzdem habe ich die Antwort aktualisiert.
Sudhir Dhumal