# 1071 - La chiave specificata era troppo lunga; la lunghezza massima della chiave è 1000 byte


103

So che le domande con questo titolo hanno già ricevuto risposta, ma per favore continua a leggere. Ho letto attentamente tutte le altre domande / risposte su questo errore prima di pubblicare.

Ricevo l'errore precedente per la seguente query:

CREATE TABLE IF NOT EXISTS `pds_core_menu_items` (
  `menu_id` varchar(32) NOT NULL,
  `parent_menu_id` int(32) unsigned DEFAULT NULL,
  `menu_name` varchar(255) DEFAULT NULL,
  `menu_link` varchar(255) DEFAULT NULL,
  `plugin` varchar(255) DEFAULT NULL,
  `menu_type` int(1) DEFAULT NULL,
  `extend` varchar(255) DEFAULT NULL,
  `new_window` int(1) DEFAULT NULL,
  `rank` int(100) DEFAULT NULL,
  `hide` int(1) DEFAULT NULL,
  `template_id` int(32) unsigned DEFAULT NULL,
  `alias` varchar(255) DEFAULT NULL,
  `layout` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`menu_id`),
  KEY `index` (`parent_menu_id`,`menu_link`,`plugin`,`alias`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Qualcuno ha idea del perché e come risolverlo? Il problema è che questa stessa query funziona perfettamente sulla mia macchina locale e ha funzionato anche sul mio host precedente. Btw.it proviene da un progetto maturo - phpdevshell - quindi immagino che questi ragazzi sappiano cosa stanno facendo, anche se non si sa mai.

Qualsiasi indizio apprezzato.

Sto usando phpMyAdmin.

Risposte:


171

Come dice @Devart, la lunghezza totale del tuo indice è troppo lunga.

La risposta breve è che non dovresti comunque indicizzare colonne VARCHAR così lunghe, perché l'indice sarà molto voluminoso e inefficiente.

La procedura migliore consiste nell'usare gli indici del prefisso in modo da indicizzare solo una sottostringa sinistra dei dati. La maggior parte dei tuoi dati sarà comunque molto più breve di 255 caratteri.

È possibile dichiarare una lunghezza del prefisso per colonna mentre si definisce l'indice. Per esempio:

...
KEY `index` (`parent_menu_id`,`menu_link`(50),`plugin`(50),`alias`(50))
...

Ma qual è la migliore lunghezza del prefisso per una data colonna? Ecco un metodo per scoprirlo:

SELECT
 ROUND(SUM(LENGTH(`menu_link`)<10)*100/COUNT(`menu_link`),2) AS pct_length_10,
 ROUND(SUM(LENGTH(`menu_link`)<20)*100/COUNT(`menu_link`),2) AS pct_length_20,
 ROUND(SUM(LENGTH(`menu_link`)<50)*100/COUNT(`menu_link`),2) AS pct_length_50,
 ROUND(SUM(LENGTH(`menu_link`)<100)*100/COUNT(`menu_link`),2) AS pct_length_100
FROM `pds_core_menu_items`;

Ti dice la proporzione di righe che non hanno più di una data lunghezza di stringa nella menu_linkcolonna. Potresti vedere un output come questo:

+---------------+---------------+---------------+----------------+
| pct_length_10 | pct_length_20 | pct_length_50 | pct_length_100 |
+---------------+---------------+---------------+----------------+
|         21.78 |         80.20 |        100.00 |         100.00 |
+---------------+---------------+---------------+----------------+

Questo ti dice che l'80% delle tue stringhe contiene meno di 20 caratteri e tutte le tue stringhe sono meno di 50 caratteri. Quindi non è necessario indicizzare più di una lunghezza del prefisso di 50 e certamente non è necessario indicizzare l'intera lunghezza di 255 caratteri.

PS: i tipi di dati INT(1)e INT(32)indicano un altro malinteso su MySQL. L'argomento numerico non ha alcun effetto relativo all'archiviazione o all'intervallo di valori consentito per la colonna. INTè sempre di 4 byte e consente sempre valori da -2147483648 a 2147483647. L'argomento numerico riguarda il riempimento dei valori durante la visualizzazione, che non ha alcun effetto a meno che non si utilizzi l' ZEROFILLopzione.


17
Grazie mille per la spiegazione dettagliata. Oltre a risolvere un problema, ho anche imparato qualcosa di prezioso.
CodeVirtuoso

Query davvero molto utile per scoprire la lunghezza a cui deve essere impostato l'indice. L'ho usato alcune volte per determinare la lunghezza migliore per un indice. Grazie per aver condiviso!
Niraj Kumar

1
C'è un sottile bug nella tua pratica query per misurare la lunghezza reale delle stringhe: stai assumendo che le stringhe siano tutte presenti; cioè che sono tutti lì. Se alcuni sono nulli, i calcoli verranno eliminati e le stringhe brevi verranno sottostimate. Desideri utilizzare count ([field_name]) invece di count (*).
D Mac

Non userà più del primo indice "prefisso".
Rick James

28

Questo errore significa che la lunghezza dell'indice indexè superiore a 1000 byte. MySQL e i motori di archiviazione potrebbero avere questa limitazione. Ho ricevuto un errore simile su MySQL 5.5 - 'La chiave specificata era troppo lunga; la lunghezza massima della chiave è 3072 byte quando viene eseguito questo script:

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

UTF8 è multibyte e la lunghezza della chiave viene calcolata in questo modo: 500 * 3 * 6 = 9000 byte.

Ma nota, la prossima query funziona!

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

... perché ho usato CHARSET = latin1, in questo caso la lunghezza della chiave è 500 * 6 = 3000 byte.


7
Grazie per la risposta, funziona, ma a costo di rinunciare al set di caratteri utf8. Questa restrizione può in qualche modo essere superata (ho accesso completo al server), c'è per una buona ragione?
CodeVirtuoso

4
Questo è un consiglio pessimo, per favore impara cosa sono i set di caratteri e come ciò causerà problemi importanti in futuro. Un database con set di caratteri misto non solo causa problemi quando si utilizzano join o sotto-selezioni, ma inserisce i dati in un formato non normalizzato e può essere quasi impossibile correggerli in seguito.
Geoffrey

17

Ho riscontrato questo problema e l'ho risolto seguendo:

Causa

Esiste un bug noto con MySQL relativo a MyISAM, al set di caratteri UTF8 e agli indici che puoi controllare qui.

Risoluzione

  • Assicurati che MySQL sia configurato con il motore di archiviazione InnoDB.

  • Modificare lo storage engine utilizzato per impostazione predefinita in modo che le nuove tabelle vengano sempre create in modo appropriato:

    set GLOBAL storage_engine='InnoDb';

  • Per MySQL 5.6 e versioni successive, utilizza quanto segue:

    SET GLOBAL default_storage_engine = 'InnoDB';

  • Infine assicurati di seguire le istruzioni fornite in Migrazione a MySQL .

Riferimento


Nella maggior parte dei casi, ci dimentichiamo di configurare il motore di archiviazione come "InnoDB". La seguente risposta è la più semplice e probabilmente risolverà il problema per la maggior parte degli utenti qui. Grazie.
Frederiko Cesar

10

eseguire questa query prima di creare o modificare la tabella.

SET @@global.innodb_large_prefix = 1;

questo imposterà la lunghezza massima della chiave a 3072 byte


3

Questo limite di dimensione dell'indice sembra essere maggiore nelle build a 64 bit di MySQL.

Stavo colpendo questa limitazione cercando di scaricare il nostro database di sviluppo e caricarlo su un VMWare virt locale. Alla fine ho capito che il server di sviluppo remoto era a 64 bit e avevo creato un virt a 32 bit. Ho appena creato un virt a 64 bit e sono stato in grado di caricare il database localmente.


2

Ho appena fatto bypassare questo errore semplicemente modificando i valori della "lunghezza" nel database originale per un totale di circa "1000" cambiando la sua struttura, e quindi esportando la stessa, sul server. :)


0

Stavo affrontando lo stesso problema, utilizzato sotto la query per risolverlo.

Durante la creazione del DB è possibile utilizzare la codifica utf-8

per esempio. create database my_db character set utf8 collate utf8mb4;

EDIT: (considerando i suggerimenti dai commenti) modificato utf8_bin in utf8mb4


2
Questo mi ha indirizzato nella giusta direzione. Per me ho dovuto cambiare le mie regole di confronto in: utf8_general_ci
Ian Newland

2
NON è la giusta direzione, non farlo! MySQL utf8NON utf8lo è, è un formato proprietario con bug che non avrebbe mai dovuto realizzarsi. utf8mb4è vero utf8ed è l'impostazione predefinita consigliata per un utf8supporto adeguato .
Geoffrey

utf8mb4è la codifica corretta da usare su mysql e nonutf8
alok

Era solo un esempio - comunque ho aggiornato la risposta.
Sudhir Dhumal
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.