Errore MySQL: specifica della chiave senza lunghezza della chiave


363

Ho una tabella con una chiave primaria che è un varchar (255). Sono sorti alcuni casi in cui 255 caratteri non sono sufficienti. Ho provato a cambiare il campo in un testo, ma viene visualizzato il seguente errore:

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

come posso risolvere questo?

modifica: dovrei anche sottolineare che questa tabella ha una chiave primaria composita con più colonne.


9
Una tabella non può avere più chiavi primarie. Vuoi dire che ha una chiave primaria composita (che include più di una colonna) o ha più UNIQUEchiavi?
Quassnoi,

1
Nel mio caso, per qualche motivo, avevo un tipo TEXT per una colonna e-mail invece di VARCHAR.
Kris

Utilizzare VARCHAR per alfanumerico univoco.
JWC

Risposte:


571

L'errore si verifica perché MySQL può indicizzare solo i primi N caratteri di un BLOB o TEXTcolonna. Quindi, l'errore si verifica soprattutto quando v'è un tipo di campo / colonna TEXTo di BLOB o di quelli appartengono TEXTo BLOBtipi come TINYBLOB, MEDIUMBLOB, LONGBLOB, TINYTEXT, MEDIUMTEXT, e LONGTEXTche si tenta di effettuare una chiave primaria o un indice. Con il valore completo BLOBo TEXTsenza lunghezza, MySQL non è in grado di garantire l'unicità della colonna in quanto ha dimensioni variabili e dinamiche. Pertanto, quando si utilizza BLOBo TEXTdigita come indice, è necessario fornire il valore di N in modo che MySQL possa determinare la lunghezza della chiave. Tuttavia, MySQL non supporta un limite di lunghezza della chiave su TEXTo BLOB. TEXT(88)semplicemente non funzionerà.

L'errore verrà inoltre visualizzato quando si tenta di convertire una colonna di tabella da non-TEXTe non-BLOBdigitare come VARCHARe ENUMin TEXTo BLOBdigitare, con la colonna già definita come vincoli o indice univoci. Il comando Modifica tabella SQL avrà esito negativo.

La soluzione al problema consiste nel rimuovere la TEXTo BLOBcolonna dall'indice o vincolo univoco o impostare un altro campo come chiave primaria. Se non riesci a farlo, e vuoi posizionare un limite sulla colonna TEXTo BLOB, prova a usare VARCHARtype e posiziona un limite di lunghezza su di esso. Per impostazione predefinita, VARCHARè limitato a un massimo di 255 caratteri e il suo limite deve essere specificato in modo implicito all'interno di una parentesi dopo la sua dichiarazione, ovvero VARCHAR(200)lo limiterà a soli 200 caratteri.

A volte, anche se non si utilizza TEXTo il BLOBtipo correlato nella tabella, potrebbe apparire anche l'errore 1170. Succede in una situazione come quando specifichi la VARCHARcolonna come chiave primaria, ma imposti erroneamente la sua lunghezza o dimensione dei caratteri. VARCHARaccetta solo fino a 256 caratteri, quindi qualsiasi cosa come VARCHAR(512)forzerà MySQL a convertirlo automaticamente VARCHAR(512)in un SMALLTEXTtipo di dati, che successivamente fallisce con l'errore 1170 sulla lunghezza della chiave se la colonna viene utilizzata come chiave primaria o indice univoco o non univoco. Per risolvere questo problema, specificare un valore inferiore a 256 come dimensione per il VARCHARcampo.

Riferimento: errore MySQL 1170 (42000): colonna BLOB / TEXT utilizzata nelle specifiche chiave senza lunghezza chiave


13
dev.mysql.com/doc/refman/5.0/en/char.html "I valori nelle colonne VARCHAR sono stringhe di lunghezza variabile. La lunghezza può essere specificata come un valore compreso tra 0 e 255 prima di MySQL 5.0.3 e tra 0 e 65.535 nella versione 5.0.3 e successive. La lunghezza massima effettiva di un VARCHAR in MySQL 5.0.3 e successive è soggetta alla dimensione massima della riga (65.535 byte, che è condivisa tra tutte le colonne) "
umassthrower

1
Dove dici "MySQL può indicizzare solo i primi N caratteri di una colonna BLOB o TEXT", qual è il valore di N?
jameshfisher,

2
"Quando si indicizza una colonna BLOB o TEXT, è necessario specificare una lunghezza del prefisso per l'indice." dev.mysql.com/doc/refman/5.6/en/column-indexes.html
Vinicius Pinto

86

È necessario definire quale parte iniziale di una TEXTcolonna si desidera indicizzare.

InnoDBha una limitazione di 768byte per chiave indice e non sarà possibile creare un indice più lungo di quello.

Questo funzionerà benissimo:

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

Si noti che il valore massimo della dimensione della chiave dipende dal set di caratteri della colonna. Sono 767caratteri per un set di caratteri a byte singolo LATIN1e solo 255caratteri per UTF8( MySQLutilizza soloBMP che richiedono al massimo 3byte per carattere)

Se hai bisogno che l'intera colonna sia PRIMARY KEY, calcola SHA1o MD5hash e usala come a PRIMARY KEY.


Esattamente quello che stavo cercando. Grazie!
Jonathon Hill,

La dimensione (255 qui) è davvero in caratteri e non in byte? Perché potrei immaginare che l'uso dei personaggi per UTF-8 sarebbe davvero complicato senza ulteriori benefici.
Alexis Wilke,

Scusa, non seguo la tua domanda. Dai documenti: il limite di lunghezza del prefisso della chiave di indice è 767 byte per le tabelle InnoDB che utilizzano il formato di riga REDUNDANTo COMPACT. Ad esempio, è possibile raggiungere questo limite con un indice del prefisso di colonna di oltre 255 caratteri su una TEXTo VARCHARcolonna, assumendo un set di caratteri utf8mb3 e un massimo di 3 byte per ciascun carattere. REDUNDANTed COMPACTerano gli unici formati disponibili al momento della risposta.
Quassnoi,

62

È possibile specificare la lunghezza della chiave nella richiesta di modifica della tabella, ad esempio:

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

1
Questo era esattamente ciò di cui avevo bisogno per risolvere lo stesso problema. Grazie!
Per Quested Aronsson,

2
Dovresti stare molto attento con questo approccio! Questa è forse la soluzione più semplice, ma in molte situazioni non è la migliore. La chiave è composta da due colonne e l'ordine delle colonne è importante.
MrD,

La migliore risposta qui.
George Chalhoub,

Questo risolve il mio problema, grazie mille!
dellair,

22

MySQL esclude dal finanziamento indicizzazione un valore pieno di BLOB, TEXTe lunghe VARCHARcolonne in quanto i dati in essi contenuti può essere enorme, e implicitamente indice DB sarà grande, ciò significa che nessun beneficio dall'indice.

MySQL richiede che tu definisca i primi N caratteri da indicizzare, e il trucco è scegliere un numero N che sia abbastanza lungo da dare una buona selettività, ma abbastanza corto da risparmiare spazio. Il prefisso dovrebbe essere abbastanza lungo da rendere l'indice quasi utile quanto lo sarebbe se indicizzassi l'intera colonna.

Prima di andare oltre, definiamo alcuni termini importanti. La selettività dell'indice è il rapporto tra i valori indicizzati distinti totali e il numero totale di righe . Ecco un esempio per la tabella di test:

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

Se indicizziamo solo il primo carattere (N = 1), la tabella dell'indice sarà simile alla seguente tabella:

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

In questo caso, la selettività dell'indice è uguale a IS = 1/3 = 0,33.

Vediamo ora cosa accadrà se aumentiamo il numero di caratteri indicizzati a due (N = 2).

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

In questo scenario IS = 2/3 = 0,66 significa che abbiamo aumentato la selettività dell'indice, ma abbiamo anche aumentato la dimensione dell'indice. Il trucco è trovare il numero minimo N che porterà alla massima selettività dell'indice .

Esistono due approcci che è possibile eseguire calcoli per la tabella del database. Farò dimostrazione su questo dump del database .

Supponiamo di voler aggiungere all'indice la colonna last_name negli impiegati della tabella e vogliamo definire il numero N più piccolo che produrrà la migliore selettività dell'indice.

Innanzitutto cerchiamo di identificare i cognomi più frequenti:

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)

Come puoi vedere, il cognome Baba è il più frequente. Ora troveremo i prefissi last_name che si verificano più frequentemente , iniziando con i prefissi a cinque lettere.

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

Ci sono molte più occorrenze di ogni prefisso, il che significa che dobbiamo aumentare il numero N fino a quando i valori sono quasi gli stessi dell'esempio precedente.

Ecco i risultati per 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    |
+-----+-----------+

Ecco i risultati per 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)

Questi sono ottimi risultati. Ciò significa che possiamo creare un indice su colonna last_nameindicizzando solo i primi 10 caratteri. Nella tabella definizione colonna last_nameè definito come VARCHAR(16), e questo significa che abbiamo salvato 6 byte (o più se ci sono caratteri UTF8 nel cognome) per voce. In questa tabella ci sono 1637 valori distinti moltiplicati per 6 byte è di circa 9 KB e immagina come questo numero aumenterebbe se la nostra tabella contiene milioni di righe.

Puoi leggere altri modi per calcolare il numero di N nel mio post Indici prefissati in MySQL .


3
Questo non è stato abbastanza aggiornato. Ho trovato molto più facile da capire rispetto alla risposta accettata
Mawg dice di ripristinare Monica il

10

Ho riscontrato questo errore durante l'aggiunta di un indice a una tabella con colonne di tipo testo. È necessario dichiarare l'importo della dimensione che si desidera utilizzare per ciascun tipo di testo.

Inserisci l'importo della dimensione tra parentesi ()

Se vengono utilizzati troppi byte, è possibile dichiarare una dimensione tra parentesi per varchar per ridurre la quantità utilizzata per l'indicizzazione. Questo anche se hai dichiarato una dimensione per un tipo già come varchar (1000). Non è necessario creare una nuova tabella come altri hanno detto.

Aggiunta di un indice

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

Aggiunta di un indice univoco

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

Risposta più semplice credo e ha funzionato subito per me. Grazie.
Matt Cremeens,


4

Un altro modo eccellente di gestire questo è creare il campo TEXT senza il vincolo univoco e aggiungere un campo VARCHAR fratello unico che contiene un digest (MD5, SHA1, ecc.) Del campo TEXT. Calcola e memorizza il digest sull'intero campo TESTO quando inserisci o aggiorni il campo TESTO, quindi hai un vincolo di unicità sull'intero campo TESTO (piuttosto che una parte iniziale) che può essere cercato rapidamente.


1
Dovresti anche stare molto attento con stringhe completamente “casuali”, come quelle prodotte da MD5 (), SHA1 () o UUID (). Ogni nuovo valore generato con essi verrà distribuito in modo arbitrario su un ampio spazio, che può rallentare INSERT e alcuni tipi di query SELECT:
MrD

2
La distribuzione di MD5, SHA1 su dati non dannosi dovrebbe essere uniforme --- ecco a cosa servono gli hash.
jb.

sarebbe bello se potessi fornire qualche esempio.
WebComer

3

Non avere valori lunghi come chiave primaria. Ciò distruggerà la tua esibizione. Vedere il manuale di mysql, sezione 13.6.13 'Ottimizzazione e risoluzione dei problemi di InnoDB'.

Invece, avere una chiave int surrogata come primaria (con auto_increment) e la tua chiave loong come UNIQUE secondaria.


2

Aggiungi un'altra colonna varChar (255) (con impostazione predefinita come stringa vuota non nulla) per contenere l'overflow quando 255 caratteri non sono sufficienti e modifica questo PK per utilizzare entrambe le colonne. Tuttavia, questo non suona come uno schema di database ben progettato, e consiglierei a un modellatore di dati di guardare ciò che hai in vista di riformattarlo per una maggiore normalizzazione.


2

La soluzione al problema è che nella CREATE TABLEdichiarazione è possibile aggiungere il vincolo UNIQUE ( problemtextfield(300) )dopo che la colonna ha creato le definizioni per specificare una keylunghezza di 300caratteri per un TEXTcampo, ad esempio. Quindi i primi 300caratteri del problemtextfield TEXTcampo dovrebbero essere unici e tutte le differenze successive verranno ignorate.


1

Inoltre, se si desidera utilizzare l'indice in questo campo, è necessario utilizzare il motore di archiviazione MyISAM e il tipo di indice FULLTEXT.


Potresti considerare di aggiungere una spiegazione e un collegamento alla documentazione.
LeeGee,

1

Nessuno lo ha menzionato finora ... con utf8mb4 che è a 4 byte e può anche contenere emoticon (non dovremmo mai usare utf8 a 3 byte) e possiamo evitare errori come Incorrect string value: \xF0\x9F\x98\...non dovremmo usare il tipico VARCHAR (255) ma piuttosto VARCHAR ( 191) perché nel caso utf8mb4 e VARCHAR (255) la stessa parte dei dati è archiviata off-page e non è possibile creare un indice per la colonna VARCHAR (255) ma per VARCHAR (191) è possibile. È perché la dimensione massima della colonna indicizzata è 767 byte per ROW_FORMAT = COMPACT o ROW_FORMAT = REDUNDANT.

Per i formati di riga più recenti ROW_FORMAT = DYNAMIC o ROW_FORMAT = COMPRESSED (che richiede un formato file più recente innodb_file_format = Barracuda non precedente Antelope) la dimensione massima della colonna indicizzata è 3072. È disponibile da MySQL> = 5.6.3 quando innodb_large_prefix = 1 (disabilitato per impostazione predefinita MySQL <= 5.7.6 e abilitato per impostazione predefinita per MySQL> = 5.7.7). Quindi in questo caso possiamo usare VARCHAR (768) per utf8mb4 (o VARCHAR (1024) per il vecchio utf8) per colonna indicizzata. L'opzione innodb_large_prefix è obsoleta dal 5.7.7 perché il suo comportamento è integrato in MySQL 8 (in questa versione è stata rimossa l'opzione).


0

Devi cambiare il tipo di colonna in varcharo integerper indicizzare.


Si potrebbe prendere in considerazione l'aggiunta di una spiegazione e un collegamento alla documentazione.
LeeGee,


-1

Usa così

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

Si potrebbe prendere in considerazione l'aggiunta di una spiegazione e un collegamento alla documentazione.
LeeGee,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.