Ошибка MySQL: спецификация ключа без длины ключа

363

У меня есть таблица с первичным ключом, который является varchar (255). Возникло несколько случаев, когда 255 символов недостаточно. Я попытался изменить поле на текст, но я получаю следующую ошибку:

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

Как я могу это исправить?

редактировать: я также должен отметить, что эта таблица имеет составной первичный ключ с несколькими столбцами.

GSto
источник
9
Таблица не может иметь несколько первичных ключей. Вы имеете в виду, что он имеет составной первичный ключ (который включает в себя более одного столбца) или имеет несколько UNIQUEключей?
Quassnoi
1
В моем случае по какой-то причине у меня был тип TEXT для столбца электронной почты вместо 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 Error 1170 (42000): столбец BLOB / TEXT, используемый в спецификации ключа без длины ключа

OMG пони
источник
13
dev.mysql.com/doc/refman/5.0/en/char.html "Значения в столбцах VARCHAR являются строками переменной длины. Длина может быть указана в виде значения от 0 до 255 до MySQL 5.0.3 и от 0 до 65 535 в 5.0.3 и более поздних версиях. Эффективная максимальная длина VARCHAR в MySQL 5.0.3 и более поздних версиях зависит от максимального размера строки (65 535 байт, который является общим для всех столбцов) "
umassthrower
1
Где вы говорите: «MySQL может индексировать только первые N символов столбца BLOB или TEXT», каково значение N?
jameshfisher
2
«При индексировании столбца BLOB или TEXT необходимо указать длину префикса для индекса». dev.mysql.com/doc/refman/5.6/ru/column-indexes.html
Виниций Пинто,
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.

Quassnoi
источник
Именно то, что я искал. Спасибо!
Джонатон Хилл
Размер (здесь 255) действительно в символах, а не в байтах? Потому что я мог представить, что использование символов для UTF-8 будет действительно сложным без каких-либо дополнительных преимуществ.
Алексис Вилке
Извините, я не слежу за вашим вопросом. Из документов: Предел длины префикса ключа индекса составляет 767 байт для таблиц InnoDB, которые используют формат строки REDUNDANTили COMPACT. Например, вы можете установить этот предел с индексом префикса столбца более 255 символов для столбца TEXTили VARCHAR, предполагая набор символов utf8mb3 и максимум 3 байта для каждого символа. REDUNDANTи COMPACTбыли единственными форматами, доступными в то время, когда был дан этот ответ.
Quassnoi
62

Вы можете указать длину ключа в запросе alter table, например:

alter table authors ADD UNIQUE(name_first(20), name_second(20));
Майк Эванс
источник
1
Это было именно то, что мне нужно, чтобы решить ту же проблему. Спасибо!
За Квестед Аронссон
2
Вы должны быть очень осторожны с этим подходом! Это, пожалуй, самое простое решение, но в большинстве случаев не самое лучшее. Ваш ключ состоит из двух столбцов, и порядок столбцов важен.
MrD
Лучший ответ здесь.
Джордж Чалхуб
Это решает мою проблему, большое спасибо!
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 из таблицы employee в индекс, и мы хотим определить наименьшее число 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)

Как видите, фамилия Баба - самая частая. Теперь мы собираемся найти наиболее часто встречающиеся префиксы last_name , начиная с пятибуквенных префиксов.

+-----+--------+
| 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 .

MrD
источник
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));
хитрость
источник
Самый простой ответ, который я считаю, и он сразу сработал. Спасибо.
Мэтт Кременс
7
alter table authors ADD UNIQUE(name_first(767), name_second(767));

ПРИМЕЧАНИЕ : 767 - это количество символов, до которого MySQL будет индексировать столбцы при работе с BLOB / текстовыми индексами.

Ссылка: http://dev.mysql.com/doc/refman/5.7/en/innodb-restrictions.html

Абхишек Гоэль
источник
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 символов, и измените этот PK, чтобы использовать оба столбца. Однако это не похоже на хорошо спроектированную схему базы данных, и я бы порекомендовал получить средство моделирования данных, чтобы взглянуть на то, что у вас есть, с целью его рефакторинга для большей нормализации.

Чарльз Бретана
источник
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 not old Antelope) максимальный размер индексированного столбца составляет 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 (в этой версии опция удалена).

mikep
источник
0

Вы должны изменить тип столбца на varcharили integerдля индексации.

Маной Мишра
источник
Вы можете добавить объяснение и ссылку на документацию.
LeeGee
0

Перейти к MySQL edit table-> изменить тип столбца на varchar(45).

Маной Мишра
источник
-1

Используйте как это

@Id
@Column(name = "userEmailId", length=100)
private String userEmailId;
Кришна дас
источник
Вы можете добавить объяснение и ссылку на документацию.
LeeGee