Ошибка MySQL: спецификация ключа без длины ключа - PullRequest
320 голосов
/ 01 декабря 2009

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

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

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

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

Ответы [ 15 ]

508 голосов
/ 01 декабря 2009

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

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

77 голосов
/ 01 декабря 2009

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

56 голосов
/ 12 июля 2011

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

alter table authors ADD UNIQUE(name_first(20), name_second(20));
19 голосов
/ 22 ноября 2015

MySQL запрещает индексирование полного значения столбцов BLOB, TEXT и длинных VARCHAR, поскольку содержащиеся в них данные могут быть огромными, и неявно индекс БД будет большим, что означает отсутствие выгоды от индекса.

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)

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

6 голосов
/ 06 апреля 2016
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

3 голосов
/ 01 марта 2012

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

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

2 голосов
/ 28 сентября 2015

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

1 голос
/ 25 мая 2019

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

Укажите размер в скобках ()

Если используется слишком много байтов, вы можете объявить размер в скобках для 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));
1 голос
/ 01 декабря 2009

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

0 голосов
/ 15 февраля 2019

Никто еще не упомянул об этом ... с 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 (в этой версии опция удалена).

...