# 1071 - Określony klucz był za długi; maksymalna długość klucza wynosi 767 bajtów

562

Kiedy wykonałem następujące polecenie:

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

Otrzymałem ten komunikat o błędzie:

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

Informacje o kolumnie 1 i kolumnie 2:

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

Myślę, że varchar(20)wymaga tylko 21 bajtów, a varchar(500)tylko 501 bajtów. Więc łączna liczba bajtów to 522, mniej niż 767. Dlaczego więc dostałem komunikat o błędzie?

#1071 - Specified key was too long; max key length is 767 bytes
Steven
źródło
5
Ponieważ nie jest to 520 bajtów, ale 2080 bajtów, co znacznie przekracza 767 bajtów, można wykonać kolumnę varchar (20) i kolumnę varchar (170). jeśli chcesz
ekwiwalentu
3
myślę, że twoje obliczenia są tu trochę niepoprawne. mysql używa 1 lub 2 dodatkowych bajtów do zapisania długości wartości: 1 bajt, jeśli maksymalna długość kolumny wynosi 255 bajtów lub mniej, 2, jeśli jest dłuższy niż 255 bajtów. kodowanie utf8_general_ci wymaga 3 bajtów na znak, więc varchar (20) używa 61 bajtów, varchar (500) używa 1502 bajtów łącznie 1563 bajtów
brakovic10
3
mysql> wybierz maxlen, nazwa_zestawu_znaków z informacji_schema.character_sets gdzie nazwa_zestawu_znaków w ('latin1', 'utf8', 'utf8mb4'); szczaw | nazwa_zestawu_znaków ------ | ------------------- 1 | latin1 ------ | ------------------- 3 | utf8 ------ | ------------------- 4 | utf8mb4
brakovic10
15
„jeśli chcesz ekwiwalentu znak / bajt, użyj latin1” Nie rób tego . Latin1 naprawdę, naprawdę do bani. Pożałujesz tego.
Stijn de Witt,
Rozwiązanie można znaleźć na stronie stackoverflow.com/a/52778785/2137210
Pratik

Odpowiedzi:

490

767 bajtów to podane ograniczenie prefiksu dla tabel InnoDB w MySQL w wersji 5.6 (i wcześniejszych wersjach). Dla tabel MyISAM ma 1000 bajtów. W MySQL w wersji 5.7 i wyższych limit ten został zwiększony do 3072 bajtów.

Musisz także pamiętać, że jeśli ustawisz indeks na duże pole char lub varchar, które jest zakodowane w utf8mb4, musisz podzielić maksymalną długość prefiksu indeksu wynoszącą 767 bajtów (lub 3072 bajtów) przez 4, co daje wynik 191. Jest tak, ponieważ maksymalna długość znaku utf8mb4 wynosi cztery bajty. Dla znaku utf8 byłyby to trzy bajty, co daje maksymalną długość prefiksu indeksu 254.

Jedną z opcji jest po prostu dolny limit na polach VARCHAR.

Inną opcją (zgodnie z odpowiedzią na ten problem ) jest uzyskanie podzbioru kolumny zamiast całej kwoty, tj .:

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

Dostosuj, aby uzyskać klucz do zastosowania, ale zastanawiam się, czy warto byłoby przejrzeć model danych dotyczący tego podmiotu, aby sprawdzić, czy istnieją ulepszenia, które pozwoliłyby na wdrożenie zamierzonych reguł biznesowych bez naruszania ograniczeń MySQL.

Kucyki OMG
źródło
4
Aby zastosować, określając podzbiór kolumny zamiast całej kwoty. Dobre rozwiązanie.
Steven
204
To nie wyjaśnia, dlaczego pola znacznie poniżej limitu długości przekraczają limit długości ...
Cerin
6
Próbowałem edytować informacje, których brakuje @Cerin powyżej, co wyraźnie uważano za brakujące również przez innych, ale jest odrzucane jako bardziej odpowiednie jako komentarz. Ci, którzy próbują zrozumieć, dlaczego 500 + 20> 767, zobacz komentarz Stefana Endrullisa do odpowiedzi Juliena.
Letharion
8
To może być problem. Na przykład: Mam nazwę pola (255) i dodaję unikatowe do tego pola pod nazwą (191), ponieważ używam utf8mb4. Jeśli mój użytkownik doda swoją nazwę za pomocą „IJUE3ump5fiUuCi16jSofYS234MLschW4wsIktKiBrTPOTKBK6Vteh5pNuz1tKjy ... aO500mlJs”, a inny użytkownik doda swoją nazwę za pomocą tego „IJUE3ump5fiUuCi16jSJWKWSBKWWSBZWKBSWKWSWKJWKWSJWKWSJWKWSWKJWKWSJWK. Powinien przejść walidację, nie utknąwszy przy zduplikowanym wpisie.
vee
28
Limit indeksu wynosi 767 bajtów , a nie znaków. A ponieważ utf8mb4zestaw znaków Mysql (który reszta świata nazywa utf8) potrzebuje (maksymalnie) 4 bajtów na znak , można indeksować tylko do VARCHAR(191). utf8Zestaw znaków Mysql (który reszta świata nazywa uszkodzonymi) potrzebuje maksymalnie 3 bajtów na znak, więc jeśli go używasz (czego nie powinieneś ), możesz indeksować doVARCHAR(255)
Stijn de Witt
403

Jeśli ktoś ma problemy z INNODB / Utf-8 próbującym umieścić UNIQUEindeks w VARCHAR(256)polu, przełącz go na VARCHAR(255). Wydaje się, że 255 jest ograniczeniem.

PinkTurtle
źródło
246
Liczba dozwolonych znaków zależy tylko od zestawu znaków. UTF8 może wykorzystywać do 3 bajtów na znak, utf8mb4 do 4 bajtów, a latin1 tylko 1 bajt. Zatem dla utf8 twoja długość klucza jest ograniczona do 255 znaków, ponieważ 3*255 = 765 < 767.
Stefan Endrullis
9
To zaoszczędziło mi wiele frustracji - dziękuję. Powinna to być zaakceptowana odpowiedź IMO, biorąc pod uwagę, że użytkownik korzysta z InnoDB, twierdząc, że przekroczył limit 767b.
TheCarver,
8
Jak stwierdził Stefan Endrullis, zależy to od zestawu znaków. Jeśli używasz UTF8, który wykorzystuje 3 bajty: 255x3 = 765, który jest niższy niż limit 767, a 256x3 = 768, który jest wyższy. Ale jeśli użyjesz UTF8mb4, jego 255 * 4 = 1020, więc nie jest to naprawdę rozwiązanie
Bernhardh
25
Ta odpowiedź jest poprawna. Jeśli jednak 255 naprawdę działa dla Ciebie, oznacza to, że używasz MySQL utf8, który niestety jest zepsuty. Może kodować tylko znaki w podstawowej płaszczyźnie wielojęzycznej. Pojawią się problemy z postaciami, które będą poza tym. Na przykład te znaki Emoji, które dodają, są poza tym, jak sądzę. Więc zamiast przełączać się na VARCHAR(255), przełącz się na VARCHAR(191) i przełącz kodowanie na utf8mb4(co w rzeczywistości jest po prostu utf8, ale MySql chciał się wycofać.
Stijn de Witt,
1
Nie jest to pomocne w przypadku mojego unikalnego wielokolumnowego ograniczenia. Nie działałoby to również w przypadku wielokolumnowego ograniczenia OP. (dałby całkowity rozmiar 825 bajtów)
Barett
351

Kiedy osiągniesz limit. Ustaw następujące.

  • INNODB utf8 VARCHAR(255)
  • INNODB utf8mb4 VARCHAR(191)
Aley
źródło
34
To najlepsza odpowiedź. Prosty, od razu do utf8mb4rzeczy, a także zawiera limit (który jest najczęściej używanym kodowaniem w nowszych bazach danych, ponieważ akceptuje emoji / etc).
Claudio Holanda,
10
ponieważ 767/4 ~ = 191 i 767/3 ~ = 255
Księgowy م
11
gdzie i jak to ustawić?
Dinesh Sunny,
1
Tak, podając ENGINE=InnoDB DEFAULT CHARSET=utf8na końcu CREATE TABLEinstrukcji, że mogłem mieć VARCHAR(255)klucz podstawowy. Dzięki.
xonya
1
ktoś daje mu +1, aby przekroczyć limit 191 :)
cagcak
147

MySQL zakłada najgorszy przypadek liczby bajtów na znak w ciągu. W przypadku kodowania MySQL „utf8” jest to 3 bajty na znak, ponieważ kodowanie to nie dopuszcza znaków poza nim U+FFFF. W przypadku kodowania „utf8mb4” MySQL jest to 4 bajty na znak, ponieważ tak właśnie MySQL nazywa rzeczywisty UTF-8.

Zakładając, że używasz „utf8”, twoja pierwsza kolumna zajmie 60 bajtów indeksu, a druga następna 1500.

morganwahl
źródło
4
- Co prawdopodobnie oznacza, że ​​używając utf8mb4, muszę ustawić je na (najwyżej) 191 jako 191 * 4 = 764 <767.
Isaac
2
@Isaac Tak, dokładnie
morganwahl
2
Myślę, że to może być właściwa odpowiedź, ale czy mógłbyś wyjaśnić, co trzeba zrobić, aby rozwiązać taki problem? Przynajmniej dla noobów MySQL takich jak ja?
Adam Grant,
Nie ma jednego sposobu na obejście tego limitu indeksu. Pytanie dotyczy unikalnych ograniczeń; w przypadku nich możesz mieć jedną kolumnę tekstu o nieograniczonej długości, a drugą, w której przechowujesz skrót (np. MD5) tego tekstu, i wykorzystujesz kolumnę skrótu do unikalnego ograniczenia. Musisz upewnić się, że Twój program aktualizuje skróty, gdy zmienia tekst, ale istnieją różne sposoby radzenia sobie z tym bez większego problemu. Szczerze mówiąc, MySQL powinien sam zaimplementować coś takiego, abyś nie musiał; Nie zdziwiłbym się, gdyby MariaDB miała coś takiego.
morganwahl
Unikalny indeks na bardzo długiej kolumnie varchar jest rzadki. Zastanów się, dlaczego go potrzebujesz, ponieważ może to być problem z projektem. Jeśli chcesz po prostu indeks do wyszukiwania, rozważ pole „słów kluczowych”, które mieści się w obrębie 191 znaków, lub podziel tekst na krótki opis i długi / pełny tekst itp. Lub jeśli naprawdę potrzebujesz wyszukiwania pełnotekstowego, rozważ użycie specjalistycznego oprogramowania do to, na przykład Apache Lucene.
Stijn de Witt
56

uruchom to zapytanie przed zapytaniem:

SET @@global.innodb_large_prefix = 1;

zwiększy to limit do 3072 bytes.

Raza Ahmed
źródło
4
Czy jest jakaś wada globalnej zmiany na innodb_large_prefix? I czy to DB jest „globalne”, czy wszystkie DB są CAŁKOWICIE GLOBALNE?
SciPhi
5
Ma zastosowanie tylko w przypadku korzystania z niestandardowych formatów wierszy. Zobacz dev.mysql.com/doc/refman/5.5/en/… . W szczególności „Włącz tę opcję, aby zezwolić na prefiksy kluczy indeksu dłuższe niż 767 bajtów (do 3072 bajtów), w przypadku tabel InnoDB, które używają formatów wierszy DYNAMIC i COMPRESSED”. Domyślny format wiersza pozostaje niezmieniony.
Chris Lear
5
Dla mnie to zadziałało - więcej szczegółów i przewodnik można znaleźć tutaj: mechanics.flite.com/blog/2014/07/29/…
cwd
1
czy po tym musimy ponownie uruchomić serwer MySQL?
SimpleGuy
7
Ważne brakujące szczegóły w tej odpowiedzi. innodb_file_formatmusi być BARRACUDAi Na poziomie stołu musisz użyć ROW_FORMAT=DYNAMIClub ROW_FORMAT=COMPRESSED. Zobacz komentarz @ cwd powyżej z przewodnikiem.
q0rban
46

Rozwiązanie dla Laravel Framework

Zgodnie z dokumentacją Laravel 5.4. * ; Musisz ustawić domyślną długość ciągu w bootmetodzie app/Providers/AppServiceProvider.phppliku w następujący sposób:

use Illuminate\Support\Facades\Schema;

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

Wyjaśnienie tej poprawki podane w dokumentacji Laravel 5.4. * :

Laravel domyślnie korzysta z utf8mb4zestawu znaków, który obejmuje obsługę przechowywania „emotikonów” w bazie danych. Jeśli używasz wersji MySQL starszej niż wersja 5.7.7 lub MariaDB starszej niż wersja 10.2.2, może być konieczne ręczne skonfigurowanie domyślnej długości ciągu generowanego przez migracje, aby MySQL mógł utworzyć dla nich indeksy. Możesz to skonfigurować, wywołując Schema::defaultStringLengthmetodę w swoim AppServiceProvider.

Alternatywnie możesz włączyć innodb_large_prefixopcję dla swojej bazy danych. Instrukcje dotyczące prawidłowego włączania tej opcji znajdują się w dokumentacji bazy danych.

Ali Shaukat
źródło
10
@BojanPetkovic no właśnie wyszedłem z problemu Laravela, a ta odpowiedź faktycznie rozwiązała mój problem.
mkmnstr
Ta odpowiedź jest świetna. Ale czy ktoś wie, dlaczego dokładnie to działa?
UeliDeSchwert
1
Dokumentacja @Bobby Laravel zawiera wyjaśnienia w nagłówku „Indeks długości i MySQL / MariaDB” laravel.com/docs/5.4/migrations#creating-indexes
Ali Shaukat
1
@Bobby Zaktualizowałem odpowiedź. Dodałem wyjaśnienie podane w dokumentacji laravel dla tej poprawki.
Ali Shaukat,
39

Jakiego kodowania znaków używasz? Niektóre zestawy znaków (jak UTF-16 i tak dalej) używają więcej niż jednego bajtu na znak.

Bursztyn
źródło
8
Jeśli jest to UTF8, znak może używać do 4 bajtów, tak że kolumna 20 znaków to 20 * 4 + 1bajty, a kolumna 500 znaków to 500 * 4 + 2bajty
Thanatos
5
Za to, co jest tego warte, właśnie miałem ten sam problem i przejście z utf8_general_ci na utf8_unicode_ci rozwiązało problem dla mnie. Nie wiem jednak, dlaczego :(
Andresch Serj
11
W przypadku VARCHAR(256)kolumny z UNIQUEindeksem zmiana sortowania nie miała dla mnie żadnego efektu, tak jak w przypadku @Andresch. Jednak zmniejszenie długości z 256 do 255 to rozwiązało. Nie rozumiem dlaczego, ponieważ 767 / maks. 4 bajty na znak dają maksymalnie 191?
Arjan
10
255*3 = 765; 256*3 = 768. Wygląda na to, że twój serwer przypisywał 3 bajty na znak, @Arjan
Amber
21
@Greg: Masz rację, ale należy to rozwinąć: sam UTF-8 używa 1–4 bajtów na punkt kodowy. „Zestawy znaków” MySQL (tak naprawdę kodowanie) ma zestaw znaków o nazwie „utf8”, który jest w stanie zakodować część UTF-8 i używa 1–3 bajtów na punkt kodowy i nie jest w stanie kodować punktów kodowych poza BMP. Zawiera także inny zestaw znaków o nazwie „utf8mb4”, który wykorzystuje 1–4 bajty na punkt kodowy i jest w stanie zakodować wszystkie punkty kodowe Unicode. (utf8mb4 to UTF-8, utf8 to dziwna wersja UTF-8.)
Thanatos
28

Myślę, że varchar (20) wymaga tylko 21 bajtów, podczas gdy varchar (500) wymaga tylko 501 bajtów. Więc łączna liczba bajtów to 522, mniej niż 767. Dlaczego więc dostałem komunikat o błędzie?

UTF8 wymaga 3 bajtów na znak do przechowywania łańcucha, więc w twoim przypadku 20 + 500 znaków = 20 * 3 + 500 * 3 = 1560 bajtów, co jest więcej niż dozwolone 767 bajtów.

Limit dla UTF8 wynosi 767/3 = 255 znaków , dla UTF8mb4, który wykorzystuje 4 bajty na znak, wynosi 767/4 = 191 znaków.


Istnieją dwa rozwiązania tego problemu, jeśli chcesz użyć dłuższej kolumny niż limit:

  1. Użyj „tańszego” kodowania (tego, które wymaga mniej bajtów na znak)
    W moim przypadku musiałem dodać Unikalny indeks do kolumny zawierającej ciąg SEO artykułu, ponieważ używam tylko [A-z0-9\-]znaków dla SEO, użyłem, latin1_general_ciktóry używa tylko jednego bajtu na znak i dlatego kolumna może mieć 767 bajtów długości.
  2. Utwórz skrót z kolumny i użyj unikalnego indeksu tylko dla tej
    drugiej Inną opcją dla mnie było utworzenie kolejnej kolumny, która przechowałaby skrót SEO, ta kolumna miałaby UNIQUEklucz do zapewnienia unikalności wartości SEO. Dodałbym również KEYindeks do oryginalnej kolumny SEO, aby przyspieszyć wyszukiwanie.
Buksy
źródło
To załatwiło sprawę. Miałem varchar (256) i musiałem zmienić go na varchar (250).
MaRmAR,
25

Odpowiedź na pytanie, dlaczego pojawia się komunikat o błędzie, została już udzielona przez wielu użytkowników tutaj. Moja odpowiedź dotyczy tego, jak to naprawić i jak używać.

Zobacz ten link .

  1. Otwórz klienta MySQL (lub klienta MariaDB). Jest to narzędzie wiersza poleceń.
  2. Zostaniesz poproszony o hasło, wprowadź poprawne hasło.
  3. Wybierz bazę danych za pomocą tego polecenia use my_database_name;

Baza danych zmieniona

  1. set global innodb_large_prefix=on;

Zapytanie OK, dotyczy 0 wierszy (0,00 s)

  1. set global innodb_file_format=Barracuda;

Zapytanie OK, dotyczy 0 wierszy (0,02 s)

  1. Przejdź do swojej bazy danych na phpMyAdmin lub coś podobnego, aby ułatwić zarządzanie. > Wybierz bazę danych> Wyświetl strukturę tabeli > Przejdź do zakładki Operacje . > Zmień ROW_FORMAT na DYNAMIC i zapisz zmiany.
  2. Przejdź do zakładki struktury tabeli > Kliknij przycisk Unikatowy .
  3. Gotowy. Teraz nie powinien zawierać błędów.

Problem tej poprawki polega na tym, że eksportujesz bazę danych db na inny serwer (na przykład z hosta lokalnego na prawdziwy host) i nie możesz używać wiersza poleceń MySQL na tym serwerze. Nie możesz sprawić, żeby tam działało.

vee
źródło
jeśli nadal występuje błąd po wprowadzeniu powyższego zapytania, spróbuj przejść do „phpmyadmin”> ustaw „sortowanie” według własnych preferencji (dla mnie używam „utf8_general_ci”)> kliknij Zastosuj (nawet jeśli jest to już utf8)
Anthony Kal
Nie używam narzędzia, które działa z Twoimi instrukcjami, ale nadal głosuję za próbą pomocy ludziom w naprawieniu problemu. Istnieje nieskończone wyjaśnienie przyczyny problemu, ale niezwykle niewiele, jak go rozwiązać.
Teekin
20
Specified key was too long; max key length is 767 bytes

Otrzymałeś tę wiadomość, ponieważ 1 bajt jest równy 1 znakowi tylko wtedy, gdy używasz latin-1zestawu znaków. Jeśli użyjesz utf8, każdy znak zostanie uznany za 3 bajty podczas definiowania kolumny klucza. Jeśli użyjesz utf8mb4, każdy znak będzie traktowany jako 4 bajty podczas definiowania kolumny klucza. Dlatego musisz pomnożyć limit znaków pola klucza przez 1, 3 lub 4 (w moim przykładzie), aby określić liczbę bajtów, na którą pole klucza próbuje zezwolić. Jeśli używasz uft8mb4, możesz zdefiniować tylko 191 znaków dla natywnego pola klucza podstawowego InnoDB. Tylko nie przekraczaj 767 bajtów.

Anthony Rutledge
źródło
16

możesz dodać kolumnę md5 długich kolumn

diyizm
źródło
Pamiętaj, że nie pozwoli to na skanowanie zakresów w tych kolumnach. Długości prefiksów w zmiennej VARCHAR pozwalają zachować tę cechę, jednocześnie powodując potencjalnie fałszywe dopasowania w indeksie (oraz skanowanie i wyszukiwanie wierszy w celu ich wyeliminowania) (patrz zaakceptowana odpowiedź). (Jest to zasadniczo ręcznie zaimplementowany indeks skrótu, który niestety MysQL nie obsługuje tabel InnoDB.)
Thanatos
Nie mogłem używać indeksów prefiksowych, ponieważ musiałem zachować zgodność z H2 do celów testowych, i stwierdziłem, że użycie kolumny mieszającej działa dobrze. Chciałbym mocno zalecamy korzystanie z funkcji takich jak odporna na zderzenia SHA1 zamiast MD5 aby zapobiec niewłaściwemu użytkownikom tworzenie kolizji. Kolizję indeksu można wykorzystać do wycieku danych, jeśli jedno z zapytań sprawdza tylko wartość skrótu, a nie pełną wartość kolumny.
Mały Mike
16

Wymień utf8mb4się utf8w pliku importu.

wprowadź opis zdjęcia tutaj

Bryan
źródło
Dlaczego dokładnie należy to zrobić? Dodatkowo, jeśli ma to jakieś wady (które zakładam), należy o tym wspomnieć
Nico Haase
13

Napotkaliśmy ten problem podczas próby dodania indeksu UNIQUE do pola VARCHAR (255) przy użyciu utf8mb4. Podczas gdy problem jest już dobrze zarysowany, chciałem dodać kilka praktycznych rad, w jaki sposób wymyśliliśmy to i rozwiązaliśmy.

Gdy używasz utf8mb4, znaki liczą się jako 4 bajty, podczas gdy w utf8 mogą one wynosić 3 bajty. Bazy danych InnoDB mają taki limit, że indeksy mogą zawierać tylko 767 bajtów. Tak więc, używając utf8, możesz przechowywać 255 znaków (767/3 = 255), ale używając utf8mb4, możesz przechowywać tylko 191 znaków (767/4 = 191).

Absolutnie możesz dodawać regularne indeksy dla VARCHAR(255)pól za pomocą utf8mb4, ale dzieje się tak, że rozmiar indeksu jest automatycznie obcinany do 191 znaków - jak unique_keytutaj:

Zrzut ekranu Sequel Pro z indeksem obciętym do 191 znaków

Jest to w porządku, ponieważ zwykłe indeksy są po prostu używane, aby pomóc MySQL w szybszym wyszukiwaniu danych. Całe pole nie musi być indeksowane.

Dlaczego więc MySQL automatycznie obcina indeks dla zwykłych indeksów, ale zgłasza wyraźny błąd, próbując zrobić to dla unikalnych indeksów? Cóż, aby MySQL mógł dowiedzieć się, czy wstawiana lub aktualizowana wartość już istnieje, musi faktycznie zindeksować całą wartość, a nie tylko jej część.

Na koniec dnia, jeśli chcesz mieć unikalny indeks na polu, cała zawartość pola musi mieścić się w indeksie. W przypadku utf8mb4 oznacza to zmniejszenie długości pola VARCHAR do 191 znaków lub mniej. Jeśli nie potrzebujesz utf8mb4 dla tej tabeli lub pola, możesz upuścić go z powrotem do utf8 i mieć możliwość zachowania 255 pól długości.

maknz
źródło
11

Oto moja oryginalna odpowiedź:

Po prostu upuszczam bazę danych i odtwarzam w ten sposób, a błąd zniknął:

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

Jednak nie działa we wszystkich przypadkach.

W rzeczywistości jest to problem z użyciem indeksów w kolumnach VARCHAR z zestawem znaków utf8(lub utf8mb4), z kolumnami VARCHAR, które mają więcej niż pewną długość znaków. W przypadkuutf8mb4 tej pewnej długości wynosi 191.

Więcej informacji na temat używania długich indeksów w bazie danych MySQL znajduje się w sekcji Długi indeks: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database- zestaw znaków do utf8mb4

Châu Hồng Lĩnh
źródło
Rozwiązano problem z konfiguracją spotkań otwartych (BTW uratowałeś moją noc :-)
Ludo
11

5 obejść:

Limit został podniesiony w 5.7.7 (MariaDB 10.2.2?). I można go zwiększyć za pomocą niektórych prac w 5.6 (10.1).

Jeśli osiągasz limit z powodu próby użycia ZESTAWU ZNAKÓW utf8mb4. Następnie wykonaj jedną z następujących czynności (każda ma wadę), aby uniknąć błędu:

  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
źródło
10

Rozwiązałem ten problem z:

varchar(200) 

zastąpiony przez

varchar(191)

wszystkie varchary, które mają więcej niż 200, zastępują je 191 lub ustawiają tekst.

flik
źródło
To też działało dla mnie. Miałem varchara (250), ale dane nigdy nie były tak długie. Zmieniono na varchar (100). Dzięki za pomysł :)
Arun Basil Lal
7

Przeprowadziłem wyszukiwanie w tym temacie, ale w końcu wprowadziłem niestandardową zmianę

Dla środowiska roboczego MySQL 6.3.7 Dostępna jest graficzna międzyfazowa

  1. Uruchom Workbench i wybierz połączenie.
  2. Przejdź do zarządzania lub wystąpienia i wybierz Opcje Plik.
  3. Jeśli Workbench poprosi Cię o pozwolenie na odczytanie pliku konfiguracyjnego, a następnie zezwól na to, naciskając dwa razy przycisk OK.
  4. Na środku znajduje się okno pliku opcji administratora.
  5. Przejdź do zakładki InnoDB i sprawdź przedrostek innodb_large_prefix, jeśli nie jest zaznaczony w sekcji Ogólne.
  6. ustaw wartość opcji innodb_default_row_format na DYNAMIC.

W przypadku wersji poniżej 6.3.7 opcje bezpośrednie nie są dostępne, dlatego należy przejść do wiersza polecenia

  1. Uruchom CMD jako administrator.
  2. Idź do dyrektora, na którym jest zainstalowany serwer mysql. Większość przypadków znajduje się w folderze „C: \ Program Files \ MySQL \ MySQL Server 5.7 \ bin”, więc polecenie to „cd \” „cd Program Files \ MySQL \ MySQL Server 5.7 \ bin”.
  3. Teraz uruchom polecenie mysql -u nazwa_użytkownika -p databasescheema Teraz poprosił o hasło odpowiedniego użytkownika. Podaj hasło i wprowadź polecenie mysql.
  4. Musimy ustawić niektóre ustawienia globalne, wprowadzać poniższe polecenia jeden po drugim, ustawić globalny innodb_large_prefix = on; ustaw globalny format_pliku_wpodb = barracuda; set global innodb_file_per_table = true;
  5. Teraz w końcu musimy zmienić ROW_FORMAT wymaganej tabeli domyślnie jej COMPACT, musimy ustawić ją na DYNAMIC.
  6. użyj następującego polecenia alter table nazwa_tabeli ROW_FORMAT = DYNAMIC;
  7. Gotowy
Abhishek
źródło
Nie mogę znaleźć tego: 6. ustaw wartość opcji innodb_default_row_format na DYNAMIC.
Adrian Cid Almaguer
jeśli set global innodb_default_row_format = DYNAMIC;użyję, zobaczę ten komunikat: BŁĄD 1193 (HY000): Nieznana zmienna systemowa „innodb_default_row_format”
Adrian Cid Almaguer
jak dostałeś błąd ze stołu roboczego do cmd? Zrobiłem to ze stołu warsztatowego, który ma opcję bezpośrednio.
Abhishek
Robię to z CMD, ponieważ nie widzę opcji w Workbench
Adrian Cid Almaguer
1
Widzę problem, jaki ta odmiana została wprowadzona w wersji 5.9 i mam wersję 5.33.33, dzięki
Adrian Cid Almaguer
7

Dla laravel 5.7 do 6.0

Kroki do naśladowania

  1. Idź do App\Providers\AppServiceProvider.php.
  2. Dodaj to do dostawcy use Illuminate\Support\Facades\Schema;u góry.
  3. Wewnątrz funkcji Boot Dodaj to Schema::defaultStringLength(191);

to wszystko, ciesz się.

Manojkiran.A
źródło
Pracował również dla Laravel 5.8.
Neo
To złe rozwiązanie. Powód: indeksy nie mają być nieskończenie długie. Gdy do czegoś zastosujesz unikalny indeks, chcesz, aby indeks był stały przez większość czasu. Oznacza to, że NIE POWINNIŚCIE tworzyć emailunikatowych elementów, ale należy przesłać wiadomość e-mail i uczynić ją wyjątkową. W przeciwieństwie do nieprzetworzonych danych łańcuchowych, skróty mają stałą szerokość i mogą być indeksowane i unikalne bez problemów. Zamiast zrozumieć problem, rozpowszechniasz okropne praktyki, których nie można skalować.
NB
5

zmień swoje zestawienie. Możesz użyć utf8_general_ci, który obsługuje prawie wszystkie

Nids Barthwal
źródło
„Prawie” to całkiem dobra wskazówka, że ​​nie jest to rozwiązanie długoterminowe
Nico Haase
4

Właśnie przejście utf8mb4do utf8tworzenia tabel rozwiązało mój problem. Na przykład: CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;do CREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;.

MajidJafari
źródło
4

Aby to naprawić, działa to dla mnie jak urok.

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;
Nick Pridorozhko
źródło
Potwierdzam, że moim problemem było także sortowanie bazy danych. Nie mam na to żadnego wyjaśnienia. Korzystam z mysql v5.6.32, a zestawienie DB to utf8mb4_unicode_ci.
mahyard
2

Opierając się na kolumnie podanej poniżej, te 2 kolumny ciągów zmiennych używają utf8_general_cisortowania (utf8 sugerowany jest zestaw znaków).

W MySQL, utf8charset używa maksymalnie 3 bajtów na każdy znak. Dlatego musiałby przydzielić 500 * 3 = 1500 bajtów, czyli znacznie więcej niż 767 bajtów, na które pozwala MySQL. Dlatego pojawia się ten błąd 1071.

Innymi słowy, musisz obliczyć liczbę znaków na podstawie reprezentacji bajtów zestawu znaków, ponieważ nie każdy zestaw znaków jest reprezentacją pojedynczego bajtu (jak przypuszczałeś). IE utf8w MySQL używa maksymalnie 3 bajtów na znak, 767 / 3≈255 znaków, a dla utf8mb4maksymalnie 4-bajtowej reprezentacji 767 / 4≈191 znaków.

Wiadomo również, że MySQL

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci
Devy
źródło
1

Znalazłem to zapytanie przydatne w wykrywaniu, które kolumny mają indeks naruszający maksymalną długość:

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')
Andrzej
źródło
1

Sprawdź, czy sql_modejest jak

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

jeśli tak, zmień na

sql_mode=NO_ENGINE_SUBSTITUTION

LUB

zrestartuj serwer zmieniając plik my.cnf (wstawiając następujące)

innodb_large_prefix=on
Neel
źródło
1

W moim przypadku miałem ten problem, kiedy tworzyłem kopię zapasową bazy danych przy użyciu znaków wyjściowych / wejściowych przekierowania linuxa. Dlatego zmieniam składnię, jak opisano poniżej. PS: za pomocą terminala Linux lub Mac.

Kopia zapasowa (bez przekierowania>)

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

Przywróć (bez <przekierowania)

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

Błąd „Określony klucz był zbyt długi; maksymalna długość klucza to 767 bajtów” po prostu zniknął.

Cassio Seffrin
źródło
1

Długości indeksu i MySQL / MariaDB


Laravel domyślnie używa zestawu znaków utf8mb4 , który obejmuje obsługę przechowywania „emoji” w bazie danych. Jeśli używasz wersji MySQL starszej niż wersja 5.7.7 lub MariaDB starszej niż wersja 10.2.2, może być konieczne ręczne skonfigurowanie domyślnej długości ciągu generowanego przez migracje, aby MySQL mógł utworzyć dla nich indeksy. Możesz to skonfigurować, wywołując metodę Schema :: defaultStringLength w swoim AppServiceProvider:

use Illuminate\Support\Facades\Schema;

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

Alternatywnie możesz włączyć opcję innodb_large_prefix dla swojej bazy danych. Instrukcje dotyczące prawidłowego włączania tej opcji znajdują się w dokumentacji bazy danych.

Odnośnik z bloga: https://www.scratchcode.io/specified-key-too-long-error-in-laravel/

Odniesienie z oficjalnej dokumentacji laravel: https://laravel.com/docs/5.7/migrations

Mayank Dudakiya
źródło
0

Jeśli tworzysz coś takiego:

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;

powinno być coś takiego

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;

ale musisz sprawdzić unikalność tej kolumny z kodu lub dodać nową kolumnę jako MD5 lub SHA1 kolumny varchar

10undertiber
źródło
3
Następnie utracono Twój unikalny klucz. Myślę, że lepszym sposobem jest po prostu skrócenie długości nazwy do 191.
haudoing
1
Po co programowo sprawdzać wyjątkowość, jeśli jest to natywna funkcja DB?
Paweł Tomkiel
0

Z powodu ograniczeń prefiksów ten błąd wystąpi. 767 bajtów to podane ograniczenie prefiksu dla tabel InnoDB w wersjach MySQL starszych niż 5.7. Dla tabel MyISAM ma 1000 bajtów. W MySQL w wersji 5.7 i wyższych limit ten został zwiększony do 3072 bajtów.

Uruchomienie poniższej usługi w usłudze powodującej błąd powinno rozwiązać problem. To musi być uruchomione w CLI MYSQL.

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=on;
SET GLOBAL innodb_large_prefix=on;
Rzeki
źródło
-1

Zmień CHARSET pola indeksu reklamacji na „latin1”,
tj. ALTER TABELA tbl ZMIEŃ myfield myfield varchar (600) ZNAK ZESTAWU latin1 DEFAULT NULL;
latin1 zajmuje jeden bajt na jeden znak zamiast czterech

Stan Holodnak
źródło
5
A co, jeśli spróbuje wstawić znaki niełacińskie1 ?
Paweł Tomkiel
4
To jest najgorsze. Nigdy tego nie rób.
Sebas,
1
w moim przypadku jest to kolumna, która przechowuje nazwy ścieżek zawierające tylko znaki szesnastkowe ... więc może to być rozwiązanie dla kogoś. to naprawdę zależy od tego, co chcesz przechowywać ...
codewandler
Musiałem to zanotować, ponieważ używanie latin1 nie jest rozwiązaniem w 2016AD. Nadal mam okropne koszmary o czasie, kiedy w 2007 roku musieliśmy przekonwertować bazę danych z latin1 na utf8. Ugh. Nie ładna W ogóle.
Bet Lamed
Miałem ten sam problem z unikalnym indeksem w dwóch kolumnach, w których przechowuję adresy bitcoin i identyfikatory transakcji. Zawsze będą to znaki ASCII, więc w tych kolumnach użyję latin1. Upvoting.
alexg
-2

Jeśli zmieniłeś się innodb_log_file_sizeostatnio, spróbuj przywrócić poprzednią wartość, która działała.

AnkitK
źródło