Помилка MySQL: специфікація ключа без довжини ключа


363

У мене є таблиця з первинним ключем, який є варчаром (255). Деякі випадки виникли, коли 255 символів недостатньо. Я спробував змінити поле на текст, але я отримав таку помилку:

BLOB/TEXT column 'message_id' used in key specification without a key length

як я можу це виправити?

редагувати: Я також повинен зазначити, що в цій таблиці є складений первинний ключ із кількома стовпцями.


9
У таблиці не може бути декілька первинних ключів. Ви маєте на увазі, що у нього є складений первинний ключ (який включає більше одного стовпця) або він має кілька UNIQUEключів?
Quassnoi

1
У моєму випадку чомусь у мене був тип ТЕКСТУ для стовпця електронної пошти замість VARCHAR.
Кріс

Використовуйте VARCHAR для унікальних буквено-цифрових.
JWC

Відповіді:


571

Помилка трапляється тому, що MySQL може індексувати лише перші N символів BLOB або TEXTстовпця. Таким чином , помилка в основному відбувається , коли є поле типу / стовпець TEXTабо BLOB або ті , належать TEXTабо BLOBтипів , таких як TINYBLOB, MEDIUMBLOB, LONGBLOB, TINYTEXT, MEDIUMTEXT, і LONGTEXTщо ви намагаєтеся зробити первинний ключ або індекс. З повним BLOBабо TEXTбез значення довжини MySQL не в змозі гарантувати унікальність стовпця, оскільки він має змінний та динамічний розмір. Таким чином, при використанні BLOBабо TEXTвведенні в якості індексу необхідно вказати значення N, щоб MySQL міг визначити довжину ключа. Однак MySQL не підтримує обмеження довжини ключа на TEXTабо BLOB. TEXT(88)просто не буде працювати.

Помилка також з’явиться при спробі перетворити стовпчик таблиці з non-TEXTта non-BLOBтипу, наприклад, у VARCHARта ENUMв TEXTабо BLOBввести, при цьому стовпець уже визначений як унікальні обмеження чи індекс. Команда Alter Table SQL не вдасться.

Рішення проблеми полягає в тому, щоб видалити TEXTабо BLOBстовпчик з індексу або унікального обмеження або встановити інше поле як основний ключ. Якщо ви не можете цього зробити і хочете встановити ліміт на стовпчик TEXTабо BLOBстовпчик, спробуйте використовувати VARCHARтип і розмістити на ньому межу довжини. За замовчуванням VARCHARвін обмежується максимум 255 символами, і його ліміт повинен бути вказаний неявно в дужці відразу після його оголошення, тобто VARCHAR(200)обмежуватиме його лише 200 символами.

Іноді, незважаючи на те, що ви не використовуєте TEXTабо BLOBпов’язані з цим типом у своїй таблиці, також може з’явитися помилка 1170. Це відбувається в такій ситуації, як коли ви вказуєте VARCHARстовпчик як основний ключ, але неправильно встановлюєте його довжину чи розмір символів. VARCHARможе тільки приймає до 256 символів, так що нічого такого , як VARCHAR(512)змусить MySQL для автоматичного перетворити VARCHAR(512)в SMALLTEXTтип даних, який згодом зазнає невдачі з помилкою 1170 на довжину ключа , якщо стовпець використовується в якості первинного ключа або унікального або не рідкість індекс. Щоб вирішити цю проблему, вкажіть для VARCHARполя цифру менше 256 .

Довідка: Помилка MySQL 1170 (42000): стовпець BLOB / TEXT використовується в специфікації ключа без довжини ключа


13
dev.mysql.com/doc/refman/5.0/en/char.html "Значення стовпців VARCHAR - це рядки змінної довжини. До MySQL 5.0.3 та 0 до 65535 довжина може бути вказана як значення від 0 до 255 у версії 5.0.3 та новіших версіях. Ефективна максимальна довжина VARCHAR в MySQL 5.0.3 та пізніших версіях залежить від максимального розміру рядків (65535 байт, який поділяється між усіма стовпцями) "
umassthrower

1
Якщо ви говорите "MySQL може індексувати лише перші N символів стовпця BLOB або TEXT", яке значення N?
jameshfisher

2
"Коли ви індексуєте стовпець BLOB або TEXT, ви повинні вказати довжину префікса для індексу." dev.mysql.com/doc/refman/5.6/uk/column-indexes.html
Vinicius Pinto

86

Ви повинні визначити, яку провідну частину TEXTстовпця ви хочете проіндексувати.

InnoDBмає обмеження 768байт на індексний ключ, і ви не зможете створити індекс довше цього.

Це буде добре працювати:

CREATE TABLE t_length (
      mydata TEXT NOT NULL,
      KEY ix_length_mydata (mydata(255)))
    ENGINE=InnoDB;

Зауважте, що максимальне значення розміру ключа залежить від діаграми стовпців. Це 767символи для однобайтового набору, як, LATIN1і лише 255символи для UTF8( MySQLвикористовується лише те, BMPщо потрібно не більше 3байтів на символ)

Якщо вам потрібен цілий стовпець, виберіть PRIMARY KEY, обчисліть SHA1або MD5хеш і використовуйте його як PRIMARY KEY.


Саме те, що я шукав. Дякую!
Джонатан Хілл

Чи розмір (тут 255) насправді в символах, а не в байтах? Тому що я міг уявити, що використання символів для UTF-8 було б дуже складним без додаткових переваг.
Алексіс Вілке

Вибачте, я не стежу за вашим запитанням. З документа: Обмеження довжини префікса ключа індексу - 767 байт для таблиць InnoDB, які використовують формат REDUNDANTабо COMPACTрядки. Наприклад, ви можете досягти цього обмеження індексом префікса стовпця, що містить більше 255 символів на TEXTабо або VARCHARстовпчику, припускаючи набір символів utf8mb3 та максимум 3 байти для кожного символу. REDUNDANTі COMPACTбули єдиними форматами, доступними на той момент, коли ця відповідь була надана.
Quassnoi

62

Ви можете вказати довжину ключа в запиті на змінну таблицю, наприклад:

alter table authors ADD UNIQUE(name_first(20), name_second(20));

1
Це саме те, що мені потрібно було вирішити таку ж проблему. Дякую!
Запитаний Аронсон

2
Вам слід бути дуже обережним з таким підходом! Це, можливо, найпростіше рішення, але в багатьох ситуаціях не найкраще. Ваш ключ складається з двох стовпців, і порядок стовпців важливий.
ГР

Найкраща відповідь тут.
Джордж Chalhoub

Це вирішує моє питання, дуже дякую!
dellair

22

MySQL Забороняє індексації повної вартості BLOB, TEXTі довгі VARCHARстовпці , так як дані , які вони містять , може бути величезними, і неявно індекс DB будуть великими, не означають нічого не поможе індексу.

MySQL вимагає, щоб ви визначили перші N символів для індексації, а хитрість полягає у виборі числа N, яке досить довге, щоб дати гарну вибірковість, але досить коротке, щоб заощадити місце. Префікс повинен бути досить довгим, щоб зробити індекс майже таким же корисним, як це було б, якби ви індексували весь стовпець.

Перш ніж піти далі, давайте визначимося з деякими важливими термінами. Селективність індексу - це відношення загальної різної індексованої величини та загальної кількості рядків . Ось один приклад для тестової таблиці:

+-----+-----------+
| id  | value     |
+-----+-----------+
| 1   | abc       |
| 2   | abd       |
| 3   | adg       |
+-----+-----------+

Якщо ми індексуємо лише перший символ (N = 1), то таблиця індексів буде мати вигляд наступної таблиці:

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| a             | 1,2,3     |
+---------------+-----------+

У цьому випадку вибірковість індексу дорівнює IS = 1/3 = 0,33.

Давайте тепер подивимося, що буде, якщо збільшити кількість індексованих символів до двох (N = 2).

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| ab             | 1,2      |
| ad             | 3        |
+---------------+-----------+

У цьому сценарії IS = 2/3 = 0,66, це означає, що ми збільшили вибірковість індексу, але також збільшили розмір індексу. Трюк - знайти мінімальне число N, що призведе до максимальної вибірковості індексу .

Існує два підходи, за допомогою яких можна обчислити таблицю бази даних. Я продемонструю цей дамп на базі даних .

Скажімо, ми хочемо додати ім'я стовпця last_name до працівників таблиці до індексу, і ми хочемо визначити найменше число N, яке створить найкращу вибірковість індексу.

Спершу визначимо найчастіші прізвища:

select count(*) as cnt, last_name 
from employees 
group by employees.last_name 
order by cnt

+-----+-------------+
| cnt | last_name   |
+-----+-------------+
| 226 | Baba        |
| 223 | Coorg       |
| 223 | Gelosh      |
| 222 | Farris      |
| 222 | Sudbeck     |
| 221 | Adachi      |
| 220 | Osgood      |
| 218 | Neiman      |
| 218 | Mandell     |
| 218 | Masada      |
| 217 | Boudaillier |
| 217 | Wendorf     |
| 216 | Pettis      |
| 216 | Solares     |
| 216 | Mahnke      |
+-----+-------------+
15 rows in set (0.64 sec)

Як бачите, прізвище Баба є найчастішим. Тепер ми будемо знаходити префікси прізвища, що найчастіше зустрічаються , починаючи з п'ятибуквених префіксів.

+-----+--------+
| cnt | prefix |
+-----+--------+
| 794 | Schaa  |
| 758 | Mande  |
| 711 | Schwa  |
| 562 | Angel  |
| 561 | Gecse  |
| 555 | Delgr  |
| 550 | Berna  |
| 547 | Peter  |
| 543 | Cappe  |
| 539 | Stran  |
| 534 | Canna  |
| 485 | Georg  |
| 417 | Neima  |
| 398 | Petti  |
| 398 | Duclo  |
+-----+--------+
15 rows in set (0.55 sec)

Кожен префікс набагато більше, а це означає, що нам потрібно збільшити число N, поки значення майже не будуть такими ж, як у попередньому прикладі.

Ось результати для N = 9

select count(*) as cnt, left(last_name,9) as prefix 
from employees 
group by prefix 
order by cnt desc 
limit 0,15;

+-----+-----------+
| cnt | prefix    |
+-----+-----------+
| 336 | Schwartzb |
| 226 | Baba      |
| 223 | Coorg     |
| 223 | Gelosh    |
| 222 | Sudbeck   |
| 222 | Farris    |
| 221 | Adachi    |
| 220 | Osgood    |
| 218 | Mandell   |
| 218 | Neiman    |
| 218 | Masada    |
| 217 | Wendorf   |
| 217 | Boudailli |
| 216 | Cummings  |
| 216 | Pettis    |
+-----+-----------+

Ось результати для N = 10.

+-----+------------+
| cnt | prefix     |
+-----+------------+
| 226 | Baba       |
| 223 | Coorg      |
| 223 | Gelosh     |
| 222 | Sudbeck    |
| 222 | Farris     |
| 221 | Adachi     |
| 220 | Osgood     |
| 218 | Mandell    |
| 218 | Neiman     |
| 218 | Masada     |
| 217 | Wendorf    |
| 217 | Boudaillie |
| 216 | Cummings   |
| 216 | Pettis     |
| 216 | Solares    |
+-----+------------+
15 rows in set (0.56 sec)

Це дуже хороші результати. Це означає, що ми можемо робити індекс на стовпчику last_nameз індексуванням лише перших 10 символів. У таблиці визначення стовпця last_nameвизначається як VARCHAR(16), і це означає, що ми зберегли 6 байтів (або більше, якщо в прізвищі є символи UTF8) на запис. У цій таблиці 1637 різних значень, помножених на 6 байт, це приблизно 9 КБ, і уявіть, як зростало б це число, якщо наша таблиця містить мільйон рядків.

Ви можете прочитати інші способи обчислення кількості N у моєму пост Префіксні індекси в MySQL .


3
Це недостатньо оновлено. Мені
здалося, що

10

Я отримав цю помилку під час додавання покажчика до таблиці із стовпцями тексту типу. Вам потрібно оголосити розмір розміру, який ви хочете використовувати для кожного типу тексту.

Помістіть розмір розміру в круглі дужки ()

Якщо використовується занадто багато байтів, ви можете оголосити розмір у дужках для varchar, щоб зменшити кількість, використану для індексації. Це навіть якщо ви оголосили розмір для типу, який вже схожий на varchar (1000). Вам не потрібно створювати нову таблицю, як казали інші.

Додавання індексу

alter table test add index index_name(col1(255),col2(255));

Додавання унікального індексу

alter table test add unique index_name(col1(255),col2(255));

Найпростіша відповідь, я вважаю, і вона спрацювала для мене відразу. Дякую.
Метт Кремен


4

Ще один чудовий спосіб вирішення цього питання - створити своє поле TEXT без унікального обмеження та додати унікальне поле VARCHAR, яке містить дайджест (MD5, SHA1 тощо) поля TEXT. Обчислюйте та зберігайте дайджест у всьому полі TEXT, коли ви вставляєте або оновлюєте поле TEXT, тоді у вас є обмеження унікальності для всього поля TEXT (а не деякої провідної частини), яку можна швидко шукати.


1
Вам також слід бути дуже обережними з абсолютно "випадковими" рядками, такими як MD5 (), SHA1 () або UUID (). Кожне нове значення, яке ви генеруєте разом з ними, буде розподілено довільним способом на великому просторі, що може уповільнити INSERT та деякі типи SELECT запитів:
MrD

2
Розподіл MD5, SHA1 над нешкідливими даними повинен бути рівномірним --- для цього потрібні хеші.
jb.

було б чудово, якби ви могли навести якийсь приклад.
WebComer

3

Не мають довгих значень як основного ключа. Це знищить вашу ефективність. Дивіться посібник з mysql, розділ 13.6.13 "Налаштування та усунення несправностей InnoDB".

Натомість майте сурогатний ключ int як основний (з auto_increment), а ключ loong як вторинний UNIQUE.


2

Додайте інший стовпець varChar (255) (за замовчуванням, якщо порожній рядок не є нульовим), щоб утримувати переповнення, коли 255 символів недостатньо, і змініть цю ПК для використання обох стовпців. Однак це не схоже на добре розроблену схему баз даних, і я рекомендую отримати модельєр даних, щоб переглянути те, що у вас є, з метою рефакторингу його для більшої нормалізації.


2

Вирішення проблеми полягає в тому, що у вашому CREATE TABLEоператорі ви можете додати обмеження UNIQUE ( problemtextfield(300) )після того, як стовпець створить визначення, щоб вказати, наприклад, keyдовжину 300символів для TEXTполя. Тоді перші 300символи problemtextfield TEXTполя повинні бути унікальними, і будь-які відмінності після цього не будуть ігноровані.


1

Також, якщо ви хочете використовувати індекс у цьому полі, вам слід використовувати механізм зберігання даних MyISAM та тип індексу FULLTEXT.


Ви можете розглянути можливість пояснення та посилання на документацію
LeeGee

1

Ніхто досі не згадував про це ... з utf8mb4, який є 4-байтовим і також може зберігати смайлики (ми ніколи більше не повинні використовувати 3-байт utf8), і ми можемо уникати помилок, як Incorrect string value: \xF0\x9F\x98\...ми не повинні використовувати типовий VARCHAR (255), а швидше VARCHAR ( 191) тому що у випадку utf8mb4 та VARCHAR (255) однакова частина даних зберігається поза сторінкою, і ви не можете створити індекс для стовпця VARCHAR (255), але для VARCHAR (191) ви можете. Це тому, що максимальний індексований розмір стовпця становить 767 байт для ROW_FORMAT = COMPACT або ROW_FORMAT = REDUNDANT.

Для нових форматів рядків ROW_FORMAT = DYNAMIC або ROW_FORMAT = COMPRESSED (для чого потрібен новіший формат файлу innodb_file_format = Barracuda не старший Антилопа) Максимальний розмір стовпця, що індексується, становить 3072. Він доступний з MySQL> = 5.6.3, коли innodb_large_prefix = 1 (відключено за замовчуванням для MySQL <= 5.7.6 та включений за замовчуванням для MySQL> = 5.7.7). Тож у цьому випадку ми можемо використовувати VARCHAR (768) для utf8mb4 (або VARCHAR (1024) для старого utf8) для індексованої колонки. Опція innodb_large_prefix застаріла з 5.7.7, оскільки її поведінка вбудована в MySQL 8 (у цій версії параметр видалено).


0

Ви повинні змінити тип стовпця на varcharабо integerдля індексації.


Ви можете розглянути можливість пояснення та посилання на документацію
LeeGee


-1

Використовуйте так

@Id
@Column(name = "userEmailId", length=100)
private String userEmailId;

Ви можете розглянути можливість пояснення та посилання на документацію
LeeGee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.