Erro no MySQL: especificação de chave sem tamanho de chave


363

Eu tenho uma tabela com uma chave primária que é um varchar (255). Alguns casos surgiram onde 255 caracteres não são suficientes. Tentei alterar o campo para um texto, mas recebo o seguinte erro:

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

Como posso consertar isso?

editar: devo também apontar que esta tabela possui uma chave primária composta com várias colunas.


9
Uma tabela não pode ter várias chaves primárias. Você quer dizer que ela possui uma chave primária composta (que inclui mais de uma coluna) ou possui várias UNIQUEchaves?
Quassnoi 01/12/2009

11
No meu caso, por algum motivo, eu tinha um tipo TEXT para uma coluna de email em vez de VARCHAR.
Kris

Use VARCHAR para alfanumérico exclusivo.
JWC

Respostas:


571

O erro ocorre porque o MySQL pode indexar apenas os primeiros N caracteres de um BLOB ou TEXTcoluna. Assim, o erro acontece principalmente quando há um tipo de campo / coluna TEXTou BLOB ou aqueles pertencem TEXTou BLOBtipos, tais como TINYBLOB, MEDIUMBLOB, LONGBLOB, TINYTEXT, MEDIUMTEXT, e LONGTEXTque você tenta fazer uma chave primária ou índice. Com o valor total BLOBou TEXTsem o comprimento, o MySQL não pode garantir a exclusividade da coluna, pois é de tamanho variável e dinâmico. Portanto, ao usar BLOBou TEXTdigitar como índice, o valor de N deve ser fornecido para que o MySQL possa determinar o comprimento da chave. No entanto, o MySQL não suporta um limite de tamanho de chave em TEXTou BLOB. TEXT(88)simplesmente não vai funcionar.

O erro também aparecerá quando você tentar converter uma coluna da tabela non-TEXTe non-BLOBdigitar como VARCHARe ENUMpara TEXTou BLOBdigite, com a coluna já definida como restrições ou índices exclusivos. O comando Alterar tabela SQL falhará.

A solução para o problema é remover a coluna TEXTou BLOBdo índice ou restrição exclusiva ou definir outro campo como chave primária. Se você não puder fazer isso, e quiser colocar um limite na coluna TEXTou BLOB, tente usar o VARCHARtipo e coloque um limite de comprimento nela. Por padrão, VARCHARé limitado a um máximo de 255 caracteres e seu limite deve ser especificado implicitamente dentro de um colchete logo após a declaração, ou seja VARCHAR(200), o limite será de apenas 200 caracteres.

Às vezes, mesmo que você não use TEXTou BLOBtipo relacionado na sua tabela, o Erro 1170 também pode aparecer. Isso acontece em uma situação como quando você especifica VARCHARcoluna como chave primária, mas define incorretamente seu tamanho ou tamanho de caracteres. VARCHARsó pode aceitar até 256 caracteres, portanto, qualquer coisa que VARCHAR(512)forçará o MySQL a converter automaticamente VARCHAR(512)para um SMALLTEXTtipo de dados, que subsequentemente falhará com o erro 1170 no comprimento da chave se a coluna for usada como chave primária ou índice único ou não exclusivo. Para resolver esse problema, especifique um valor menor que 256 como o tamanho do VARCHARcampo.

Referência: Erro MySQL 1170 (42000): Coluna BLOB / TEXT Usada na Especificação de Chaves Sem um Comprimento de Chave


13
dev.mysql.com/doc/refman/5.0/en/char.html "Os valores nas colunas VARCHAR são cadeias de comprimento variável. O comprimento pode ser especificado como um valor de 0 a 255 antes do MySQL 5.0.3 e de 0 a 65.535 na versão 5.0.3 e versões posteriores. O comprimento máximo efetivo de um VARCHAR no MySQL 5.0.3 e posterior está sujeito ao tamanho máximo da linha (65.535 bytes, que é compartilhada entre todas as colunas) "
umassthrower

11
Onde você diz "O MySQL pode indexar apenas os primeiros N caracteres de uma coluna BLOB ou TEXT", qual é o valor de N?
Jameshfisher # 23/14

2
"Ao indexar uma coluna BLOB ou TEXT, você deve especificar um tamanho de prefixo para o índice." dev.mysql.com/doc/refman/5.6/en/column-indexes.html
Vinicius Pinto

86

Você deve definir qual parte principal de uma TEXTcoluna você deseja indexar.

InnoDBtem uma limitação de 768bytes por chave de índice e você não poderá criar um índice por mais tempo que isso.

Isso funcionará bem:

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

Observe que o valor máximo do tamanho da chave depende do conjunto de caracteres da coluna. São 767caracteres para um conjunto de caracteres de byte LATIN1único e somente 255caracteres para UTF8( MySQLapenas usos BMPque requerem no máximo 3bytes por caractere)

Se você precisar que sua coluna inteira seja a PRIMARY KEY, calcule SHA1ou MD5use hash e use-a como a PRIMARY KEY.


Exatamente o que eu estava procurando. Obrigado!
Jonathon Hill

O tamanho (255 aqui) é realmente em caracteres e não em bytes? Porque eu poderia imaginar que o uso de caracteres para UTF-8 seria realmente complicado sem benefícios adicionais.
Alexis Wilke

Desculpe, eu não sigo sua pergunta. Na documentação: O limite do comprimento do prefixo da chave de índice é de 767 bytes para as tabelas do InnoDB que usam o formato REDUNDANTou COMPACTlinha. Por exemplo, você pode atingir esse limite com um índice de prefixo de coluna com mais de 255 caracteres em uma TEXTou VARCHARcoluna, assumindo um conjunto de caracteres utf8mb3 e o máximo de 3 bytes para cada caractere. REDUNDANTe COMPACTeram os únicos formatos disponíveis no momento em que essa resposta foi dada.
Quassnoi

62

Você pode especificar o comprimento da chave na solicitação de alteração da tabela, algo como:

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

11
Era exatamente isso que eu precisava para corrigir o mesmo problema. Obrigado!
Por Queston Aronsson

2
Você deve ter muito cuidado com essa abordagem! Talvez essa seja a solução mais fácil, mas em muitas situações não a melhor. Sua chave consiste em duas colunas e a ordem das colunas é importante.
MrD 22/11/2015

Melhor resposta aqui.
George Chalhoub

Isso resolve meu problema, muito obrigado!
dellair

22

Não permite a MySQL indexação de um valor total BLOB, TEXTe longas VARCHARcolunas porque os dados que eles contêm podem ser enormes, e implicitamente índice DB será grande, o que significa que nenhum benefício a partir do índice.

O MySQL exige que você defina os primeiros N caracteres a serem indexados, e o truque é escolher um número N que seja longo o suficiente para oferecer boa seletividade, mas curto o suficiente para economizar espaço. O prefixo deve ser longo o suficiente para tornar o índice quase tão útil quanto seria se você tivesse indexado a coluna inteira.

Antes de avançarmos, vamos definir alguns termos importantes. A seletividade do índice é a proporção do total de valores indexados distintos e do número total de linhas . Aqui está um exemplo para a tabela de teste:

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

Se indexarmos apenas o primeiro caractere (N = 1), a tabela de índice será semelhante à seguinte:

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

Nesse caso, a seletividade do índice é igual a IS = 1/3 = 0,33.

Vamos agora ver o que acontecerá se aumentarmos o número de caracteres indexados para dois (N = 2).

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

Nesse cenário, IS = 2/3 = 0,66, o que significa que aumentamos a seletividade do índice, mas também aumentamos o tamanho do índice. O truque é encontrar o número mínimo N que resultará na seletividade máxima do índice .

Existem duas abordagens que você pode fazer cálculos para sua tabela de banco de dados. Farei uma demonstração no despejo deste banco de dados .

Digamos que queremos adicionar a coluna last_name nos funcionários da tabela ao índice e queremos definir o menor número N que produzirá a melhor seletividade do índice.

Primeiro, vamos identificar os sobrenomes mais frequentes:

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)

Como você pode ver, o sobrenome Baba é o mais frequente. Agora, vamos encontrar os prefixos last_name mais frequentes , começando com prefixos de cinco letras.

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

Há muito mais ocorrências de cada prefixo, o que significa que precisamos aumentar o número N até que os valores sejam quase os mesmos que no exemplo anterior.

Aqui estão os resultados para 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    |
+-----+-----------+

Aqui estão os resultados para 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)

São resultados muito bons. Isso significa que podemos criar um índice na coluna last_nameindexando apenas os 10 primeiros caracteres. Na definição da tabela, coluna last_nameé definida como VARCHAR(16), e isso significa que salvamos 6 bytes (ou mais, se houver caracteres UTF8 no sobrenome) por entrada. Nesta tabela, existem 1637 valores distintos multiplicados por 6 bytes e cerca de 9 KB, e imagine como esse número aumentaria se nossa tabela contivesse milhões de linhas.

Você pode ler outras maneiras de calcular o número de N em meus índices pré-fixados no MySQL .


3
Isso não foi atualizado o suficiente. Achei que era muito mais fácil entender do que a resposta aceita
Mawg diz que restabelece Monica

10

Eu recebi esse erro ao adicionar um índice a uma tabela com colunas de tipo de texto. Você precisa declarar a quantidade de tamanho que deseja usar para cada tipo de texto.

Coloque a quantidade de tamanho entre parênteses ()

Se forem usados ​​muitos bytes, você poderá declarar um tamanho entre colchetes para varchar para diminuir a quantidade usada para indexação. Isso ocorre mesmo se você tiver declarado um tamanho para um tipo que já seja como varchar (1000). Você não precisa criar uma nova tabela como outras pessoas disseram.

Adicionando índice

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

Adicionando índice exclusivo

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

Resposta mais simples, acredito, e funcionou para mim imediatamente. Obrigado.
Matt Cremeens


4

Outra excelente maneira de lidar com isso é criar seu campo TEXT sem a restrição exclusiva e adicionar um campo irmão VARCHAR exclusivo e que contém um resumo (MD5, SHA1 etc.) do campo TEXT. Calcule e armazene o resumo sobre todo o campo TEXTO quando você insere ou atualiza o campo TEXTO; então, há uma restrição de exclusividade sobre todo o campo TEXTO (em vez de uma parte principal) que pode ser pesquisada rapidamente.


11
Você também deve ter muito cuidado com seqüências de caracteres completamente "aleatórias", como as produzidas por MD5 (), SHA1 () ou UUID (). Cada novo valor que você gerar com eles será distribuído de maneira arbitrária em um espaço amplo, o que pode atrasar o INSERT e alguns tipos de consultas SELECT:
MrD 22/11/15

2
A distribuição do MD5, SHA1 sobre dados não maliciosos deve ser uniforme - é para isso que servem os hashes.
jb.

seria ótimo se você pudesse dar um exemplo.
WebComer

3

Não tenha valores longos como chave primária. Isso destruirá seu desempenho. Veja o manual do mysql, seção 13.6.13 'Ajuste e solução de problemas do desempenho do InnoDB'.

Em vez disso, tenha uma chave int substituta como primária (com incremento automático) e sua chave longa como uma UNIQUE secundária.


2

Adicione outra coluna varChar (255) (com o padrão como sequência vazia e não nula) para manter o estouro quando 255 caracteres não forem suficientes e altere esse PK para usar as duas colunas. No entanto, isso não parece um esquema de banco de dados bem projetado, e eu recomendaria que um modelador de dados analisasse o que você tem com o objetivo de refatorá-lo para obter mais Normalização.


2

A solução para o problema é que, em sua CREATE TABLEdeclaração, você pode adicionar a restrição UNIQUE ( problemtextfield(300) )após a coluna criar definições para especificar um keycomprimento de 300caracteres para um TEXTcampo, por exemplo. Em seguida, os primeiros 300caracteres do problemtextfield TEXTcampo precisariam ser exclusivos e quaisquer diferenças posteriores a isso seriam desconsideradas.


1

Além disso, se você quiser usar o índice nesse campo, use o mecanismo de armazenamento MyISAM e o tipo de índice FULLTEXT.


Você pode adicionar uma explicação e um link para a documentação.
LeeGee

1

Ninguém o mencionou até agora ... com utf8mb4, que tem 4 bytes e também pode armazenar emoticons (nunca devemos usar utf8 de 3 bytes) e podemos evitar erros Incorrect string value: \xF0\x9F\x98\..., pois não devemos usar o VARCHAR típico (255), mas sim o VARCHAR ( 191) porque no caso utf8mb4 e VARCHAR (255) a mesma parte dos dados é armazenada fora da página e você não pode criar um índice para a coluna VARCHAR (255), mas para VARCHAR (191) é possível. Isso ocorre porque o tamanho máximo da coluna indexada é de 767 bytes para ROW_FORMAT = COMPACT ou ROW_FORMAT = REDUNDANT.

Para formatos de linha mais recentes ROW_FORMAT = DYNAMIC ou ROW_FORMAT = COMPRESSED (o que requer um formato de arquivo mais recente innodb_file_format = Barracuda não é mais antigo Antelope) o tamanho máximo da coluna indexada é 3072. Está disponível desde o MySQL> = 5.6.3 quando innodb_large_prefix = 1 (desativado por padrão MySQL <= 5.7.6 e ativado por padrão para MySQL> = 5.7.7). Portanto, neste caso, podemos usar VARCHAR (768) para utf8mb4 (ou VARCHAR (1024) para utf8 antigo) para coluna indexada. A opção innodb_large_prefix está obsoleta desde 5.7.7 porque seu comportamento está embutido no MySQL 8 (nesta versão a opção foi removida).


0

Você precisa alterar o tipo de coluna para varcharou integerpara indexação.


Você pode adicionar uma explicação e um link para a documentação.
LeeGee 20/04

0

Vá para mysql edit table-> altere o tipo de coluna para varchar(45).


-1

Use assim

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

Você pode adicionar uma explicação e um link para a documentação.
LeeGee 20/04
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.