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

289

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

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

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

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

  • 8
    Таблица не может иметь несколько первичных ключей. Вы имеете в виду, что он имеет составной первичный ключ (который включает в себя более одного столбца) или имеет несколько UNIQUE ключей?
  • 0
    В моем случае по какой-то причине у меня был тип TEXT для столбца электронной почты вместо VARCHAR.
Теги:
mysql-error-1170

11 ответов

451
Лучший ответ

Ошибка возникает из-за того, что 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, при этом столбец уже был определенные как уникальные ограничения или индекс. Ошибка команды SQL Alter Table.

Решение проблемы состоит в том, чтобы удалить столбец TEXT или BLOB из индекса или уникального ограничения или установить другое поле в качестве первичного ключа. Если вы не можете этого сделать и хотите установить ограничение на столбец TEXT или BLOB, попробуйте использовать тип VARCHAR и поместите на него ограничение длины. По умолчанию VARCHAR ограничено максимум 255 символами, и его ограничение должно быть указано неявно в скобке сразу после его объявления, т.е. VARCHAR(200) ограничит его только 200 символами.

Иногда, даже если вы не используете родственный тип TEXT или BLOB в вашей таблице, также может появиться ошибка 1170. Это происходит в ситуации, когда вы указываете столбец VARCHAR в качестве первичного ключа, но неправильно устанавливаете его длину или размер символов. VARCHAR может принимать только до 256 символов, поэтому все, например VARCHAR(512), заставит MySQL автоматически преобразовывать тип VARCHAR(512) в тип данных SMALLTEXT, который впоследствии терпит неудачу с ошибкой 1170 на длину ключа, если используется столбец в качестве первичного ключа или уникального или не уникального индекса. Чтобы решить эту проблему, укажите размер меньше 256 в качестве размера для поля VARCHAR.

Ссылка: Ошибка MySQL 1170 (42000): столбец BLOB/TEXT, используемый в спецификации ключа без длины ключа

  • 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 байт, который является общим для всех столбцов) "
  • 1
    Где вы говорите: «MySQL может индексировать только первые N символов столбца BLOB или TEXT», каково значение N?
Показать ещё 1 комментарий
69

Вы должны определить, какую ведущую часть столбца 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.

  • 0
    Именно то, что я искал. Спасибо!
  • 0
    Действительно ли размер (255 здесь) в символах, а не в байтах? Потому что я мог представить, что использование символов для UTF-8 будет действительно сложным без каких-либо дополнительных преимуществ.
Показать ещё 1 комментарий
53

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

alter table authors ADD UNIQUE(name_first(20), name_second(20));
  • 1
    Это было именно то, что мне нужно, чтобы решить ту же проблему. Спасибо!
  • 2
    Вы должны быть очень осторожны с этим подходом! Это, возможно, самое простое решение, но в большинстве случаев не самое лучшее. Ваш ключ состоит из двух столбцов, и порядок столбцов важен.
Показать ещё 1 комментарий
16

MySQL запрещает индексирование полного значения столбцов BLOB, TEXT и long 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)

Как вы можете видеть, последнее имя Baba является самым частым. Теперь мы собираемся найти наиболее часто встречающиеся префиксы 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.

  • 1
    Это не было обновлено достаточно. Я обнаружил, что это гораздо легче понять, чем принятый ответ
4
alter table authors ADD UNIQUE(name_first(767), name_second(767));

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

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

3

Не имеет длинных значений в качестве первичного ключа. Это разрушит вашу работу. См. Руководство по mysql, раздел 13.6.13 "Настройка производительности и устранение неполадок InnoDB".

Вместо этого, иметь суррогатный ключ int как первичный (с auto_increment), а ваш ключ loong как вторичный UNIQUE.

2

Еще один отличный способ справиться с этим - создать поле TEXT без уникального ограничения и добавить уникальное поле VARCHAR для сиблинга, которое содержит дайджест (MD5, SHA1 и т.д.) поля TEXT. Вычислите и сохраните дайджест во всем поле ТЕКСТ, когда вы вставляете или обновляете поле ТЕКСТ, тогда у вас есть ограничение уникальности во всем поле ТЕКСТ (а не какая-то ведущая часть), которую можно быстро найти.

  • 1
    Вы также должны быть очень осторожны с совершенно «случайными» строками, такими как строки MD5 (), SHA1 () или UUID (). Каждое новое значение, которое вы генерируете с их помощью, будет распределяться произвольным образом по большому пространству, что может замедлить INSERT и некоторые типы запросов SELECT:
  • 2
    Распределение MD5, SHA1 по неопасным данным должно быть равномерным - вот для чего нужны хэши.
Показать ещё 1 комментарий
1

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

0

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

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

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

0

Кроме того, если вы хотите использовать индекс в этом поле, вы должны использовать механизм хранения MyISAM и индексный тип FULLTEXT.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню