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.

GSto
fonte
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

Pôneis OMG
fonte
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.

Quassnoi
fonte
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));
Mike Evans
fonte
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 .

MrD
fonte
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));
furtividade
fonte
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.

par
fonte
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.

Per Lindberg
fonte
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.

Charles Bretana
fonte
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.

Quem Dunnit
fonte
1

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

Alexander Valinurov
fonte
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).

mikep
fonte
0

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

Manoj Mishra
fonte
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).

Manoj Mishra
fonte
-1

Use assim

@Id
@Column(name = "userEmailId", length=100)
private String userEmailId;
Krishna Das
fonte
Você pode adicionar uma explicação e um link para a documentação.
LeeGee 20/04