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

562

Als ich den folgenden Befehl ausgeführt habe:

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

Ich habe diese Fehlermeldung erhalten:

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

Informationen zu Spalte1 und Spalte2:

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

Ich denke varchar(20), benötigt nur 21 Bytes, während varchar(500)nur 501 Bytes benötigt. Die Gesamtzahl der Bytes beträgt also 522, weniger als 767. Warum habe ich die Fehlermeldung erhalten?

#1071 - Specified key was too long; max key length is 767 bytes
Steven
quelle
5
Da es sich nicht um 520 Bytes handelt, sondern um 2080 Bytes, was 767 Bytes bei weitem überschreitet, können Sie column1 varchar (20) und column2 varchar (170) ausführen. Wenn Sie ein Zeichen / Byte-
Äquiv.
3
Ich denke, deine Berechnung ist hier etwas falsch. mysql verwendet 1 oder 2 zusätzliche Bytes, um die Länge der Werte aufzuzeichnen: 1 Byte, wenn die maximale Länge der Spalte 255 Byte oder weniger beträgt, 2, wenn sie länger als 255 Byte ist. Die utf8_general_ci-Codierung benötigt 3 Bytes pro Zeichen, sodass varchar (20) 61 Bytes verwendet, varchar (500) 1502 Bytes insgesamt 1563 Bytes
lackovic10
3
mysql> wähle maxlen, Zeichensatzname aus information_schema.character_sets aus, wobei Zeichensatzname in ('latin1', 'utf8', 'utf8mb4'); maxlen | Zeichensatzname ------ | ------------------- 1 | latin1 ------ | ------------------- 3 | utf8 ------ | ------------------- 4 | utf8mb4
lackovic10
15
'Wenn Sie ein Zeichen / Byte-Äquiv. möchten, verwenden Sie latin1' Bitte tun Sie dies nicht . Latin1 ist wirklich, wirklich scheiße. Das wirst du bereuen.
Stijn de Witt
Siehe stackoverflow.com/a/52778785/2137210 für die Lösung
Pratik

Antworten:

490

767 Byte ist die angegebene Präfixbeschränkung für InnoDB-Tabellen in MySQL Version 5.6 (und früheren Versionen). Es ist 1.000 Byte lang für MyISAM-Tabellen. In MySQL Version 5.7 und höher wurde diese Grenze auf 3072 Byte erhöht.

Sie müssen sich auch darüber im Klaren sein, dass Sie, wenn Sie einen Index für ein großes char- oder varchar-Feld festlegen, das utf8mb4-codiert ist, die maximale Indexpräfixlänge von 767 Bytes (oder 3072 Bytes) durch 4 teilen müssen, was zu 191 führt. Dies liegt daran Die maximale Länge eines utf8mb4-Zeichens beträgt vier Bytes. Für ein utf8-Zeichen wären es drei Bytes, was zu einer maximalen Indexpräfixlänge von 254 führt.

Eine Möglichkeit besteht darin, Ihre VARCHAR-Felder nur mit einer Untergrenze zu versehen.

Eine andere Option (entsprechend der Antwort auf dieses Problem ) besteht darin, die Teilmenge der Spalte und nicht den gesamten Betrag abzurufen, dh:

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

Optimieren Sie, wenn Sie den Schlüssel zum Anwenden benötigen, aber ich frage mich, ob es sich lohnt, Ihr Datenmodell in Bezug auf diese Entität zu überprüfen, um festzustellen, ob es Verbesserungen gibt, mit denen Sie die beabsichtigten Geschäftsregeln implementieren können, ohne die MySQL-Einschränkung zu treffen.

OMG Ponys
quelle
4
Anwendung durch Angabe einer Teilmenge der Spalte anstelle des gesamten Betrags. Eine gute Lösung.
Steven
204
Dies erklärt nicht, warum Felder weit unter dem Längenlimit das Längenlimit überschreiten ...
Cerin
6
Ich habe versucht, die Informationen zu bearbeiten, die oben bei @Cerin fehlen, was eindeutig auch von anderen als vermisst angesehen wird, aber sie werden als geeigneter als Kommentar abgelehnt. Für diejenigen, die versuchen zu verstehen, warum 500 + 20> 767, siehe Stefan Endrullis 'Kommentar zu Juliens Antwort.
Letharion
8
Dies kann ein Problem sein. Beispiel: Ich habe den Feldnamen (255) und füge diesem Feld unter dem Namen (191) eine eindeutige hinzu, wenn ich utf8mb4 verwende. Wenn mein Benutzer seinen Namen mit 'IJUE3ump5fiUuCi16jSofYS234MLschW4wsIktKiBrTPOTKBK6Vteh5pNuz1tKjy ... aO500mlJs' hinzufügt, fügt der andere Benutzer seinen Namen mit ' Es sollte die Validierung bestehen und nicht bei doppelter Eingabe hängen bleiben.
V
28
Das Indexlimit beträgt 767 Byte , keine Zeichen. Und da der utf8mb4Zeichensatz von MySQL (den der Rest der Welt utf8 nennt) (höchstens) 4 Bytes pro Zeichen benötigt , können Sie nur bis zu indizieren VARCHAR(191). Mysqls utf8Zeichensatz (den der Rest der Welt als kaputt bezeichnet) benötigt höchstens 3 Bytes pro Zeichen. Wenn Sie diesen verwenden (was Sie nicht sollten ), können Sie bis zuVARCHAR(255)
Stijn de Witt
404

Wenn jemand Probleme mit INNODB / Utf-8 hat, die versuchen, einen UNIQUEIndex für ein VARCHAR(256)Feld zu erstellen , wechseln Sie zu VARCHAR(255). Es scheint, 255 ist die Einschränkung.

PinkTurtle
quelle
247
Die Anzahl der zulässigen Zeichen hängt nur von Ihrem Zeichensatz ab. UTF8 kann bis zu 3 Bytes pro Zeichen verwenden, utf8mb4 bis zu 4 Bytes und latin1 nur 1 Byte. Daher ist Ihre Schlüssellänge für utf8 auf 255 Zeichen begrenzt, da 3*255 = 765 < 767.
Stefan Endrullis
10
Das hat mir viel Frust erspart - danke. Es sollte die akzeptierte Antwort sein, IMO, wenn man bedenkt, dass der Benutzer InnoDB verwendet, indem er behauptet, eine 767b-Beschränkung erreicht zu haben.
TheCarver
8
Wie Stefan Endrullis feststellte, hängt es vom Zeichensatz ab. Wenn Sie UTF8 verwenden, das 3 Bytes verwendet: 255x3 = 765, was niedriger als die 767-Grenze ist, während 256x3 = 768, die höher ist. Aber wenn Sie verwenden UTF8mb4 sein 255 * 4 = 1020, so dass sie nicht eine wirklich Lösung
bernhardh
25
Diese Antwort ist richtig. Doch wenn 255 ist in der Tat für Sie zu arbeiten, bedeutet dies, Sie Mysql verwenden utf8, die leider gebrochen. Es können nur Zeichen in der mehrsprachigen Grundebene codiert werden. Sie werden Probleme mit Charakteren bekommen, die außerhalb davon liegen. Zum Beispiel fallen die Emoji-Charaktere, die sie hinzugefügt haben, meiner Meinung nach nicht dazu. Anstatt zu VARCHAR(255)wechseln, wechseln Sie zu VARCHAR(191) und wechseln Sie die Codierung zu utf8mb4(was eigentlich nur utf8 ist, aber MySql wollte zurückhalten. Kompatibel).
Stijn de Witt
1
Nicht hilfreich für meine mehrspaltige eindeutige Einschränkung. Es würde auch nicht für die mehrspaltige eindeutige Einschränkung des OP funktionieren. (würde es eine Gesamtgröße von 825 Bytes geben)
Barett
351

Wenn Sie das Limit erreichen. Stellen Sie Folgendes ein.

  • INNODB utf8 VARCHAR(255)
  • INNODB utf8mb4 VARCHAR(191)
Aley
quelle
34
Dies ist die beste Antwort. Einfach, direkt auf den Punkt und enthält auch utf8mb4Limit (die am häufigsten verwendete Codierung für neuere Datenbanken, da sie Emojis / etc akzeptiert).
Claudio Holanda
10
weil 767/4 ~ = 191 und 767/3 ~ = 255
Buchhalter
11
wo und wie man es einstellt?
Dinesh Sunny
1
Ja, ich habe ENGINE=InnoDB DEFAULT CHARSET=utf8am Ende der CREATE TABLEAnweisung angegeben, dass ich einen VARCHAR(255)Primärschlüssel haben konnte . Vielen Dank.
Xonya
1
jemand gibt ihm +1, um das Limit 191 zu überschreiten :)
Cagcak
147

MySQL geht vom schlechtesten Fall für die Anzahl der Bytes pro Zeichen in der Zeichenfolge aus. Für die MySQL-Codierung 'utf8' sind das 3 Bytes pro Zeichen, da diese Codierung keine darüber hinausgehenden Zeichen zulässt U+FFFF. Für die MySQL-Codierung 'utf8mb4' sind es 4 Bytes pro Zeichen, da MySQL dies als tatsächliches UTF-8 bezeichnet.

Angenommen, Sie verwenden 'utf8', benötigt Ihre erste Spalte 60 Byte des Index und Ihre zweite weitere 1500 Byte.

Morganwahl
quelle
4
- Was vermutlich bedeutet, dass ich bei Verwendung von utf8mb4 (höchstens) 191 auf 191 * 4 = 764 <767 setzen muss.
Isaac
2
@Isaac Ja genau,
Morganwahl
2
Ich denke, dies könnte die richtige Antwort sein, aber könnten Sie näher erläutern, was zu tun wäre, um ein solches Problem zu beheben? Zumindest für MySQL-Noobs wie mich?
Adam Grant
Es gibt keine Möglichkeit, dieses Indexlimit zu umgehen. Die Frage hier ist nach einzigartigen Einschränkungen; Für diese können Sie eine Spalte mit Text unbegrenzter Länge und eine andere Spalte mit einem Hash (wie MD5) dieses Textes verwenden und die Hash-Spalte für Ihre eindeutige Einschränkung verwenden. Sie müssen sicherstellen, dass Ihr Programm die Hashes auf dem neuesten Stand hält, wenn es den Text ändert, aber es gibt verschiedene Möglichkeiten, dies ohne allzu große Probleme zu handhaben. Ehrlich gesagt sollte MySQL so etwas selbst implementieren, damit Sie es nicht müssen. Es würde mich nicht wundern, wenn MariaDB so etwas eingebaut hätte.
Morganwahl
Ein eindeutiger Index für eine sehr lange Varchar-Spalte ist selten. Überlegen Sie, warum Sie es benötigen, da es sich möglicherweise um ein Designproblem handelt. Wenn Sie nur einen Index für die Suche benötigen, sollten Sie ein Feld mit Schlüsselwörtern in Betracht ziehen, das in 191 Zeichen passt, oder Text in Kurzbeschreibung und Lang- / Volltext usw. aufteilen. Wenn Sie wirklich eine Volltextsuche benötigen, sollten Sie eine spezielle Software für verwenden es, wie Apache Lucene.
Stijn de Witt
56

Führen Sie diese Abfrage vor Ihrer Abfrage aus:

SET @@global.innodb_large_prefix = 1;

Dies erhöht die Grenze auf 3072 bytes.

Raza Ahmed
quelle
4
Gibt es einen Nachteil bei der globalen Umstellung auf innodb_large_prefix? Und ist diese DB "global" oder alle DBs VOLLSTÄNDIG GLOBAL?
SciPhi
5
Gilt nur für nicht standardmäßige Zeilenformate. Siehe dev.mysql.com/doc/refman/5.5/en/… . Insbesondere: "Aktivieren Sie diese Option, um Indexschlüsselpräfixe zuzulassen, die länger als 767 Byte (bis zu 3072 Byte) sind, für InnoDB-Tabellen, die die Zeilenformate DYNAMIC und COMPRESSED verwenden." Das Standardzeilenformat bleibt davon unberührt.
Chris Lear
5
Dies hat gut für mich funktioniert - weitere Details und eine Anleitung finden Sie hier : mechanik.flite.com/blog/2014/07/29/…
cwd
1
Müssen wir danach den MySQL-Server neu starten?
SimpleGuy
7
Wichtige fehlende Details in dieser Antwort. innodb_file_formatmuss sein BARRACUDAund auf Tabellenebene muss man ROW_FORMAT=DYNAMICoder verwenden ROW_FORMAT=COMPRESSED. Siehe den Kommentar von @ cwd oben mit der Anleitung.
Q0rban
46

Lösung für Laravel Framework

Gemäß Laravel 5.4 * Dokumentation. ; Sie müssen die Standardzeichenfolgenlänge in der bootMethode der app/Providers/AppServiceProvider.phpDatei wie folgt festlegen :

use Illuminate\Support\Facades\Schema;

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

Erläuterung dieses Fixes in Laravel 5.4. * Dokumentation :

Laravel verwendet utf8mb4standardmäßig den Zeichensatz, der die Speicherung von "Emojis" in der Datenbank unterstützt. Wenn Sie eine Version von MySQL ausführen, die älter als die Version 5.7.7 oder MariaDB älter als die Version 10.2.2 ist, müssen Sie möglicherweise die durch Migrationen generierte Standardzeichenfolgenlänge manuell konfigurieren, damit MySQL Indizes für sie erstellt. Sie können dies konfigurieren, indem Sie die Schema::defaultStringLengthMethode in Ihrem aufrufen AppServiceProvider.

Alternativ können Sie die innodb_large_prefixOption für Ihre Datenbank aktivieren . Anweisungen zum ordnungsgemäßen Aktivieren dieser Option finden Sie in der Dokumentation Ihrer Datenbank.

Ali Shaukat
quelle
10
@BojanPetkovic Nun, ich bin gerade von einem Laravel-Problem gekommen, und diese Antwort hat mein Problem tatsächlich gelöst.
mkmnstr
Diese Antwort ist großartig. Aber weiß jemand, warum genau das funktioniert?
UeliDeSchwert
1
@ Bobby Laravel Dokumentation gibt seine Erklärung in der Überschrift "Index Längen & MySQL / MariaDB" laravel.com/docs/5.4/migrations#creating-indexes
Ali Shaukat
1
@ Bobby Ich habe die Antwort aktualisiert. Ich habe die Erklärung in der Laravel-Dokumentation für dieses Update hinzugefügt.
Ali Shaukat
39

Welche Zeichenkodierung verwenden Sie? Einige Zeichensätze (wie UTF-16 usw.) verwenden mehr als ein Byte pro Zeichen.

Bernstein
quelle
8
Wenn es sich um UTF8 handelt, kann ein Zeichen bis zu 4 Bytes verwenden, sodass die Spalte mit 20 Zeichen 20 * 4 + 1Bytes und die Spalte mit 500 Zeichen 500 * 4 + 2Bytes sind
Thanatos
5
Für das, was es wert ist, hatte ich nur das gleiche Problem und der Wechsel von utf8_general_ci zu utf8_unicode_ci löste das Problem für mich. Ich weiß aber nicht warum :(
Andresch Serj
11
Bei einer VARCHAR(256)Spalte mit einem UNIQUEIndex hatte das Ändern der Sortierung für mich keine Auswirkung, wie dies bei @Andresch der Fall war. Die Reduzierung der Länge von 256 auf 255 hat es jedoch gelöst. Ich verstehe nicht warum, da 767 / max 4 Bytes pro Zeichen maximal 191 ergeben würden?
Arjan
10
255*3 = 765; 256*3 = 768. Es scheint, dass Ihr Server 3 Bytes pro Zeichen angenommen hat, @Arjan
Amber
21
@ Greg: Sie haben Recht, aber dies sollte näher erläutert werden: UTF-8 selbst verwendet 1–4 Bytes pro Codepunkt. Die "Zeichensätze" (wirklich Codierungen) von MySQL haben einen Zeichensatz namens "utf8", der einen Teil von UTF-8 codieren kann , 1–3 Bytes pro Codepunkt verwendet und keine Codepunkte außerhalb des BMP codieren kann. Es enthält auch einen weiteren Zeichensatz namens "utf8mb4", der 1–4 Bytes pro Codepunkt verwendet und alle Unicode-Codepunkte codieren kann. (utf8mb4 ist UTF-8, utf8 ist eine seltsame Version von UTF-8.)
Thanatos
28

Ich denke, varchar (20) benötigt nur 21 Bytes, während varchar (500) nur 501 Bytes benötigt. Die Gesamtzahl der Bytes beträgt also 522, weniger als 767. Warum habe ich die Fehlermeldung erhalten?

UTF8 benötigt 3 Byte pro Zeichen der Zeichenfolge zu speichern, so dass in Ihrem Fall 20 + 500 Zeichen = 20 * 3 + 500 * 3 = 1560 Bytes , die sich mehr als erlaubt 767 Bytes.

Das Limit für UTF8 liegt bei 767/3 = 255 Zeichen , für UTF8mb4, das 4 Bytes pro Zeichen verwendet, bei 767/4 = 191 Zeichen.


Es gibt zwei Lösungen für dieses Problem, wenn Sie eine längere Spalte als das Limit verwenden müssen:

  1. Verwenden Sie eine "billigere" Codierung (die weniger Bytes pro Zeichen erfordert).
    In meinem Fall musste ich einen eindeutigen Index für eine Spalte hinzufügen, die eine SEO-Artikelzeichenfolge enthält, da ich nur [A-z0-9\-]Zeichen für SEO verwende, latin1_general_cidie nur ein Byte pro Zeichen verwendet Die Spalte kann also eine Länge von 767 Bytes haben.
  2. Erstellen Sie einen Hash aus Ihrer Spalte und verwenden Sie nur einen eindeutigen Index.
    Die andere Option für mich bestand darin, eine weitere Spalte zu erstellen, in der SEO-Hash gespeichert wird. Diese Spalte enthält den UNIQUESchlüssel, um sicherzustellen, dass die SEO-Werte eindeutig sind. Ich würde auch einen KEYIndex zur ursprünglichen SEO-Spalte hinzufügen , um das Nachschlagen zu beschleunigen.
Buksy
quelle
Das hat den Trick gemacht. Ich hatte varchar (256) und musste es in varchar (250) ändern.
MaRmAR
25

Die Antwort, warum Sie eine Fehlermeldung erhalten, wurde bereits von vielen Benutzern hier beantwortet. In meiner Antwort geht es darum, wie man es repariert und so verwendet, wie es ist.

Siehe von diesem Link .

  1. Öffnen Sie den MySQL-Client (oder den MariaDB-Client). Es ist ein Befehlszeilenprogramm.
  2. Sie werden nach Ihrem Passwort gefragt und geben Ihr korrektes Passwort ein.
  3. Wählen Sie Ihre Datenbank mit diesem Befehl aus use my_database_name;

Datenbank geändert

  1. set global innodb_large_prefix=on;

Abfrage OK, 0 Zeilen betroffen (0,00 Sek.)

  1. set global innodb_file_format=Barracuda;

Abfrage OK, 0 Zeilen betroffen (0,02 Sek.)

  1. Gehen Sie für eine einfache Verwaltung zu Ihrer Datenbank auf phpMyAdmin oder ähnlichem. > Datenbank auswählen> Tabellenstruktur anzeigen > Zur Registerkarte Vorgänge wechseln. > Ändern Sie ROW_FORMAT in DYNAMIC und speichern Sie die Änderungen.
  2. Gehen Sie zur Registerkarte Struktur der Tabelle > Klicken Sie auf die Schaltfläche Eindeutig .
  3. Erledigt. Jetzt sollte es keine Fehler geben.

Das Problem dieses Fixes besteht darin, dass Sie db auf einen anderen Server exportieren (z. B. von localhost auf einen echten Host) und die MySQL-Befehlszeile auf diesem Server nicht verwenden können. Sie können es dort nicht zum Laufen bringen.

vee
quelle
Wenn Sie nach der Eingabe der obigen Abfrage immer noch einen Fehler haben, gehen Sie zu "phpmyadmin"> stellen Sie die "Kollatierung" auf Ihre Präferenz ein (für mich verwende ich "utf8_general_ci")> klicken Sie auf "Anwenden" (auch wenn es bereits utf8 ist)
Anthony Kal
Ich verwende kein Tool, das mit Ihren Anweisungen funktioniert, aber ich stimme immer noch dafür, dass ich versucht habe, Menschen bei der tatsächlichen Behebung des Problems zu helfen . Es gibt endlose Erklärungen, was das Problem verursacht, aber bemerkenswert wenig darüber, wie es tatsächlich gelöst werden kann.
Teekin
20
Specified key was too long; max key length is 767 bytes

Sie haben diese Nachricht erhalten, weil 1 Byte nur dann 1 Zeichen entspricht, wenn Sie den latin-1Zeichensatz verwenden. Wenn Sie verwenden utf8, wird jedes Zeichen bei der Definition Ihrer Schlüsselspalte als 3 Byte betrachtet. Wenn Sie verwenden utf8mb4, wird jedes Zeichen bei der Definition Ihrer Schlüsselspalte als 4 Byte betrachtet. Daher müssen Sie die Zeichenbeschränkung Ihres Schlüsselfelds mit 1, 3 oder 4 (in meinem Beispiel) multiplizieren, um die Anzahl der Bytes zu bestimmen, die das Schlüsselfeld zulassen möchte. Wenn Sie uft8mb4 verwenden, können Sie nur 191 Zeichen für ein natives InnoDB-Primärschlüsselfeld definieren. Nur nicht 767 Bytes verletzen.

Anthony Rutledge
quelle
16

Sie können eine Spalte des MD5 von langen Spalten hinzufügen

DIYismus
quelle
Beachten Sie, dass Sie auf diese Weise keine Bereichsscans über diese Spalten durchführen können. Mit Präfixlängen in VARCHARs können Sie dieses Merkmal beibehalten und möglicherweise falsche Übereinstimmungen im Index verursachen (und beim Scannen und Nachschlagen von Zeilen diese entfernen) (siehe die akzeptierte Antwort). (Dies ist im Wesentlichen ein manuell implementierter Hash-Index, den MysQL leider nicht mit InnoDB-Tabellen unterstützt.)
Thanatos
Ich konnte keine Präfixindizes verwenden, da ich zu Testzwecken die Kompatibilität mit H2 aufrechterhalten musste und feststellte, dass die Verwendung einer Hash-Spalte gut funktioniert. Ich würde dringend empfehlen, eine kollisionssichere Funktion wie SHA1 anstelle von MD5 zu verwenden, um zu verhindern, dass böswillige Benutzer Kollisionen verursachen. Eine Indexkollision kann ausgenutzt werden, um Daten zu verlieren, wenn eine Ihrer Abfragen nur den Hashwert und nicht den vollständigen Spaltenwert überprüft.
Kleiner Mike
16

Ersetzen Sie utf8mb4mit utf8in der Importdatei.

Geben Sie hier die Bildbeschreibung ein

Bryan
quelle
Warum genau sollte man das tun? Wenn dies irgendwelche Nachteile hat (was ich annehme), sollten Sie dies zusätzlich erwähnen
Nico Haase
13

Dieses Problem trat auf, als versucht wurde, einem VARCHAR (255) -Feld mit utf8mb4 einen EINZIGARTIGEN Index hinzuzufügen. Während das Problem hier bereits gut umrissen ist, wollte ich einige praktische Ratschläge hinzufügen, wie wir dies herausgefunden und gelöst haben.

Bei Verwendung von utf8mb4 zählen Zeichen als 4 Bytes, während sie unter utf8 als 3 Bytes gelten können. InnoDB-Datenbanken haben ein Limit, dass Indizes nur 767 Bytes enthalten dürfen. Wenn Sie utf8 verwenden, können Sie 255 Zeichen (767/3 = 255) speichern. Mit utf8mb4 können Sie jedoch nur 191 Zeichen (767/4 = 191) speichern.

VARCHAR(255)Mit utf8mb4 können Sie durchaus reguläre Indizes für Felder hinzufügen. Die Indexgröße wird jedoch automatisch auf 191 Zeichen gekürzt - wie unique_keyhier:

Sequel Pro-Screenshot mit einem Index mit 191 Zeichen

Dies ist in Ordnung, da reguläre Indizes nur verwendet werden, um MySQL dabei zu unterstützen, Ihre Daten schneller zu durchsuchen. Das gesamte Feld muss nicht indiziert werden.

Warum schneidet MySQL den Index für reguläre Indizes automatisch ab, gibt jedoch einen expliziten Fehler aus, wenn Sie versuchen, dies für eindeutige Indizes zu tun? Damit MySQL herausfinden kann, ob der eingefügte oder aktualisierte Wert bereits vorhanden ist, muss der gesamte Wert indiziert werden und nicht nur ein Teil davon.

Wenn Sie am Ende des Tages einen eindeutigen Index für ein Feld haben möchten, muss der gesamte Inhalt des Felds in den Index passen. Für utf8mb4 bedeutet dies, dass Sie Ihre VARCHAR-Feldlängen auf 191 Zeichen oder weniger reduzieren. Wenn Sie für diese Tabelle oder dieses Feld nicht utf8mb4 benötigen, können Sie es auf utf8 zurücksetzen und Ihre Felder mit einer Länge von 255 behalten.

maknz
quelle
11

Hier ist meine ursprüngliche Antwort:

Ich lösche einfach die Datenbank und erstelle sie neu, und der Fehler ist verschwunden:

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

Es funktioniert jedoch nicht in allen Fällen.

Es ist tatsächlich ein Problem, Indizes für VARCHAR-Spalten mit dem Zeichensatz utf8(oder utf8mb4) zu verwenden, wobei VARCHAR-Spalten mehr als eine bestimmte Länge von Zeichen haben. Im Fall von utf8mb4beträgt diese bestimmte Länge 191.

Weitere Informationen zur Verwendung langer Indizes in der MySQL-Datenbank finden Sie im Abschnitt "Langer Index" in diesem Artikel: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database- Zeichensatz-zu-utf8mb4

Châu Hồng Lĩnh
quelle
Das Problem für das Openmeetings-Setup wurde behoben (Übrigens, Sie haben meine Nacht gerettet :-)
Ludo
11

5 Problemumgehungen:

Das Limit wurde in 5.7.7 (MariaDB 10.2.2?) Erhöht. Und es kann mit etwas Arbeit in 5.6 (10.1) erhöht werden.

Wenn Sie das Limit erreichen, weil Sie versuchen, CHARACTER SET utf8mb4 zu verwenden. Führen Sie dann einen der folgenden Schritte aus (jeder hat einen Nachteil), um den Fehler zu vermeiden:

  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

Rick James
quelle
10

Ich habe dieses Problem behoben mit:

varchar(200) 

Ersetzt mit

varchar(191)

Alle Varchar, die mehr als 200 haben, ersetzen sie durch 191 oder setzen sie als Text.

flik
quelle
Das hat auch bei mir funktioniert. Ich hatte einen Varchar (250), aber die Daten waren nie so lang. Es wurde in varchar (100) geändert. Danke für die Idee :)
Arun Basil Lal
7

Ich habe nach diesem Thema gesucht und endlich eine benutzerdefinierte Änderung erhalten

Für die MySQL Workbench 6.3.7 Version ist eine grafische Zwischenphase verfügbar

  1. Starten Sie Workbench und wählen Sie die Verbindung aus.
  2. Gehen Sie zu Verwaltung oder Instanz und wählen Sie Optionsdatei.
  3. Wenn Workbench Sie um Erlaubnis zum Lesen der Konfigurationsdatei bittet und diese dann durch zweimaliges Drücken von OK zulässt.
  4. In der Mitte kommt das Fenster mit den Administratoroptionen.
  5. Gehen Sie zur Registerkarte InnoDB und überprüfen Sie das innodb_large_prefix, falls es nicht im Abschnitt Allgemein aktiviert ist.
  6. Setzen Sie den Optionswert innodb_default_row_format auf DYNAMIC.

Für Versionen unter 6.3.7 sind keine direkten Optionen verfügbar, daher muss die Eingabeaufforderung verwendet werden

  1. Starten Sie CMD als Administrator.
  2. Gehen Sie zu Director, wo der MySQL-Server installiert ist. In den meisten Fällen befindet er sich unter "C: \ Programme \ MySQL \ MySQL Server 5.7 \ bin". Der Befehl lautet also "cd \" "CD-Programme \ MySQL \ MySQL Server 5.7 \ bin".
  3. Führen Sie jetzt den Befehl mysql -u userName -p databaseasescheema aus. Nun wurde nach dem Kennwort des jeweiligen Benutzers gefragt. Geben Sie ein Passwort ein und geben Sie die MySQL-Eingabeaufforderung ein.
  4. Wir müssen einige globale Einstellungen vornehmen. Geben Sie die folgenden Befehle nacheinander ein. Set global innodb_large_prefix = on; set global innodb_file_format = barracuda; set global innodb_file_per_table = true;
  5. Als letztes müssen wir das ROW_FORMAT der erforderlichen Tabelle standardmäßig ändern. COMPACT müssen wir auf DYNAMIC setzen.
  6. Verwenden Sie den folgenden Befehl alter table table_name ROW_FORMAT = DYNAMIC;
  7. Erledigt
Abhishek
quelle
Ich kann dies nicht finden: 6. Setzen Sie den Optionswert innodb_default_row_format auf DYNAMIC.
Adrian Cid Almaguer
Wenn ich benutze, wird folgende set global innodb_default_row_format = DYNAMIC;Meldung angezeigt: FEHLER 1193 (HY000): Unbekannte Systemvariable 'innodb_default_row_format'
Adrian Cid Almaguer
Wie haben Sie Fehler von Workbench zu Cmd bekommen? Ich habe es von der Workbench aus gemacht, es hat die Option direkt.
Abhishek
Ich mache es von CMD, weil ich die Option in Workbench nicht sehe
Adrian Cid Almaguer
1
Ich sehe das Problem, dass diese Variable in v5.7.9 eingeführt wurde und ich habe v5.6.33 Version, danke
Adrian Cid Almaguer
7

Für Laravel 5.7 bis 6.0

Schritte zu folgen

  1. Gehe zu App\Providers\AppServiceProvider.php.
  2. Fügen Sie dies dem Anbieter use Illuminate\Support\Facades\Schema;oben hinzu.
  3. Innerhalb der Boot-Funktion Fügen Sie dies hinzu Schema::defaultStringLength(191);

das alles, viel Spaß.

Manojkiran.A
quelle
Arbeitete auch für Laravel 5.8.
Neo
Dies ist eine schlechte Lösung. Grund: Indizes sollen nicht unendlich lang sein. Wenn Sie einen eindeutigen Index auf etwas anwenden, soll der Index die meiste Zeit fixiert sein. Das heißt , Sie sollten Sie nicht solche Sachen machen emaileinzigartig, aber Sie sollten die E - Mail - Hash und ein einzigartig machen. Im Gegensatz zu rohen String-Daten haben Hashes eine feste Breite und können ohne Probleme indiziert und eindeutig gemacht werden. Anstatt das Problem zu verstehen, verbreiten Sie schreckliche Praktiken, die nicht skalierbar sind.
NB
5

Ändern Sie Ihre Sortierung. Sie können utf8_general_ci verwenden , das fast alle unterstützt

Nids Barthwal
quelle
"Fast" ist ein ziemlich guter Hinweis darauf, dass dies keine langfristige Lösung ist
Nico Haase
4

Nur beim Erstellen von Tabellen zu wechseln utf8mb4, utf8löste mein Problem. Zum Beispiel: CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;bis CREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;.

MajidJafari
quelle
4

Um das zu beheben, funktioniert das für mich wie ein Zauber.

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;
Nick Pridorozhko
quelle
Ich bestätige, dass mein Problem auch die Datenbanksortierung war. Ich habe keine Erklärung dafür. Ich verwende MySQL v5.6.32 und die DB-Sortierung war utf8mb4_unicode_ci.
Mahyard
2

Basierend auf der unten angegebenen Spalte verwenden diese beiden Spalten mit variablen Zeichenfolgen eine utf8_general_ciSortierung ( utf8Zeichensatz ist impliziert).

In MySQL utf8verwendet charset maximal 3 Bytes für jedes Zeichen. Daher müsste es 500 * 3 = 1500 Bytes zuweisen, was viel größer ist als die 767 Bytes, die MySQL zulässt. Deshalb wird dieser 1071-Fehler angezeigt.

Mit anderen Worten, Sie müssen die Zeichenanzahl basierend auf der Bytedarstellung des Zeichensatzes berechnen, da nicht jeder Zeichensatz eine Einzelbyte-Darstellung ist (wie Sie angenommen haben). IE utf8in MySQL verwendet höchstens 3 Byte pro Zeichen, 767/3≈255 Zeichen und für utf8mb4eine höchstens 4-Byte-Darstellung 767 / 4≈191 Zeichen.

Es ist auch bekannt, dass MySQL

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci
Devy
quelle
1

Ich fand diese Abfrage hilfreich, um festzustellen, welche Spalten einen Index hatten, der die maximale Länge verletzte:

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')
Andrew
quelle
1

Bitte überprüfen Sie, ob dies der Fall sql_modeist

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

Wenn dies der Fall ist, wechseln Sie zu

sql_mode=NO_ENGINE_SUBSTITUTION

ODER

Starten Sie Ihren Server neu und ändern Sie Ihre my.cnf-Datei.

innodb_large_prefix=on
neel
quelle
1

In meinem Fall hatte ich dieses Problem, als ich eine Datenbank mit den Ausgabe- / Eingabezeichen für die Linux-Umleitung sicherte. Daher ändere ich die Syntax wie unten beschrieben. PS: mit einem Linux- oder Mac-Terminal.

Backup (ohne die> Weiterleitung)

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

Wiederherstellen (ohne die <Weiterleitung)

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

Der Fehler "Angegebener Schlüssel war zu lang; maximale Schlüssellänge beträgt 767 Bytes" ist einfach verschwunden.

Cassio Seffrin
quelle
1

Indexlängen & MySQL / MariaDB


Laravel verwendet standardmäßig den Zeichensatz utf8mb4 , der die Speicherung von "Emojis" in der Datenbank unterstützt. Wenn Sie eine Version von MySQL ausführen, die älter als die Version 5.7.7 oder MariaDB älter als die Version 10.2.2 ist, müssen Sie möglicherweise die durch Migrationen generierte Standardzeichenfolgenlänge manuell konfigurieren, damit MySQL Indizes für sie erstellt. Sie können dies konfigurieren, indem Sie die Schema :: defaultStringLength- Methode in Ihrem AppServiceProvider aufrufen:

use Illuminate\Support\Facades\Schema;

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

Alternativ können Sie die Option innodb_large_prefix für Ihre Datenbank aktivieren. Anweisungen zum ordnungsgemäßen Aktivieren dieser Option finden Sie in der Dokumentation Ihrer Datenbank.

Referenz aus dem Blog: https://www.scratchcode.io/specified-key-too-long-error-in-laravel/

Referenz aus der offiziellen Laravel-Dokumentation: https://laravel.com/docs/5.7/migrations

Mayank Dudakiya
quelle
0

Wenn Sie etwas erstellen wie:

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;

es sollte so etwas sein

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;

Sie müssen jedoch die Eindeutigkeit dieser Spalte anhand des Codes überprüfen oder eine neue Spalte als MD5 oder SHA1 der varchar-Spalte hinzufügen

10undertiber
quelle
3
Dann ist Ihr eindeutiger Schlüssel verloren. Ich denke, der bessere Weg ist einfach, die Länge des Namens auf 191 zu reduzieren.
Haudoing
1
Warum programmgesteuert auf Eindeutigkeit prüfen, ob es sich um eine native DB-Funktion handelt?
Paweł Tomkiel
0

Aufgrund von Präfixbeschränkungen tritt dieser Fehler auf. 767 Byte ist die angegebene Präfixbeschränkung für InnoDB-Tabellen in MySQL-Versionen vor 5.7. Es ist 1.000 Byte lang für MyISAM-Tabellen. In MySQL Version 5.7 und höher wurde diese Grenze auf 3072 Byte erhöht.

Wenn Sie für den Dienst, der Ihnen den Fehler gibt, Folgendes ausführen, sollte das Problem behoben sein. Dies muss in der MYSQL-CLI ausgeführt werden.

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=on;
SET GLOBAL innodb_large_prefix=on;
Flüsse
quelle
-1

Ändern Sie CHARSET des beschwerdeführenden Indexfelds in "latin1",
dh ALTER TABLE tbl CHANGE myfield myfield varchar (600) CHARACTER SET latin1 DEFAULT NULL;
latin1 benötigt ein Byte für ein Zeichen anstelle von vier

Stan Holodnak
quelle
5
Und was ist, wenn er versucht, nicht-lateinische Zeichen einzufügen ?
Paweł Tomkiel
4
Dies ist das Schlimmste. TU das niemals.
Sebas
1
In meinem Fall befindet es sich in einer Spalte, in der Pfadnamen mit nur hexadezimalen Zeichen gespeichert sind. Dies könnte also eine Lösung für jemanden sein. es hängt wirklich davon ab, was Sie speichern möchten ...
Codewandler
Ich musste dies ablehnen, da die Verwendung von latin1 in 2016AD keine Lösung ist. Ich habe immer noch schreckliche Albträume über die Zeit, als wir 2007 eine Datenbank von latin1 auf utf8 konvertieren mussten. Ugh. Nicht hübsch. Überhaupt.
Bet Lamed
Ich hatte das gleiche Problem mit einem eindeutigen Index für zwei Spalten, in denen ich Bitcoin-Adressen und Transaktions-IDs speichere. Dies sind immer ASCII-Zeichen, daher werde ich latin1 für diese Spalten verwenden. Upvoting.
Alexg
-2

Wenn Sie innodb_log_file_sizekürzlich Änderungen vorgenommen haben , versuchen Sie, den vorherigen Wert wiederherzustellen, der funktioniert hat.

AnkitK
quelle