Kesalahan MySQL: spesifikasi kunci tanpa panjang kunci

363

Saya punya tabel dengan kunci utama yaitu varchar (255). Beberapa kasus muncul di mana 255 karakter tidak cukup. Saya mencoba mengubah bidang menjadi teks, tetapi saya mendapatkan kesalahan berikut:

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

bagaimana saya bisa memperbaikinya?

sunting: Saya juga harus menunjukkan tabel ini memiliki kunci primer komposit dengan beberapa kolom.

GSto
sumber
9
Tabel tidak dapat memiliki beberapa kunci utama. Apakah maksud Anda memiliki kunci primer komposit (yang mencakup lebih dari satu kolom) atau memiliki beberapa UNIQUEkunci?
Quassnoi
1
Dalam kasus saya karena alasan tertentu saya memiliki tipe TEXT untuk kolom email, bukan VARCHAR.
Kris
Gunakan VARCHAR untuk alfanumerik unik.
JWC

Jawaban:

571

Kesalahan terjadi karena MySQL hanya dapat mengindeks karakter N pertama dari BLOB atau TEXTkolom. Jadi Kesalahan terutama terjadi ketika ada jenis bidang / kolom TEXTatau blob atau mereka milik TEXTatau BLOBjenis seperti TINYBLOB, MEDIUMBLOB, LONGBLOB, TINYTEXT, MEDIUMTEXT, dan LONGTEXTbahwa Anda mencoba untuk membuat kunci primer atau indeks. Dengan nilai penuh BLOBatau TEXTtanpa panjang, MySQL tidak dapat menjamin keunikan kolom karena ukurannya variabel dan dinamis. Jadi, ketika menggunakan BLOBatau TEXTmengetik sebagai indeks, nilai N harus disediakan sehingga MySQL dapat menentukan panjang kunci. Namun, MySQL tidak mendukung batas panjang kunci pada TEXTatau BLOB. TEXT(88)tidak akan bekerja.

Kesalahan juga akan muncul ketika Anda mencoba untuk mengkonversi kolom tabel dari non-TEXTdan non-BLOBketik seperti VARCHARdan ENUMke TEXTatau BLOBketik, dengan kolom yang sudah didefinisikan sebagai batasan atau indeks unik. Perintah Alter Table SQL akan gagal.

Solusi untuk masalah ini adalah menghapus kolom TEXTatau BLOBdari indeks atau batasan unik atau mengatur bidang lain sebagai kunci utama. Jika Anda tidak dapat melakukan itu, dan ingin menempatkan batas pada kolom TEXTatau BLOB, coba gunakan VARCHARjenis dan tempatkan batas panjang di atasnya. Secara default, VARCHARdibatasi hingga maksimum 255 karakter dan batasnya harus ditentukan secara implisit dalam braket setelah deklarasi, yaitu VARCHAR(200)akan membatasi hanya untuk 200 karakter saja.

Kadang-kadang, meskipun Anda tidak menggunakan TEXTatau BLOBjenis terkait di tabel Anda, Kesalahan 1170 juga dapat muncul. Ini terjadi dalam situasi seperti ketika Anda menentukan VARCHARkolom sebagai kunci utama, tetapi salah mengatur panjang atau ukuran karakter. VARCHARhanya dapat menerima hingga 256 karakter, sehingga hal seperti itu VARCHAR(512)akan memaksa MySQL untuk secara otomatis mengkonversi VARCHAR(512)ke SMALLTEXTtipe data, yang kemudian gagal dengan kesalahan 1170 pada panjang kunci jika kolom digunakan sebagai kunci utama atau indeks unik atau non-unik. Untuk mengatasi masalah ini, tentukan angka kurang dari 256 sebagai ukuran untuk VARCHARbidang.

Referensi: Kesalahan MySQL 1170 (42000): Kolom BLOB / TEXT Digunakan dalam Spesifikasi Kunci Tanpa Panjang Kunci

OMG Ponies
sumber
13
dev.mysql.com/doc/refman/5.0/en/char.html "Nilai dalam kolom VARCHAR adalah string panjang variabel. Panjangnya dapat ditentukan sebagai nilai dari 0 hingga 255 sebelum MySQL 5.0.3, dan 0 hingga 65.535 dalam versi 5.0.3 dan yang lebih baru. Panjang maksimum efektif VARCHAR di MySQL 5.0.3 dan yang lebih baru tunduk pada ukuran baris maksimum (65.535 byte, yang dibagi di antara semua kolom) "
umassthrower
1
Di mana Anda mengatakan "MySQL hanya dapat mengindeks karakter N pertama dari kolom BLOB atau TEXT", berapakah nilai N?
jameshfisher
2
"Ketika Anda mengindeks kolom BLOB atau TEXT, Anda harus menentukan panjang awalan untuk indeks." dev.mysql.com/doc/refman/5.6/en/column-indexes.html
Vinicius Pinto
86

Anda harus menentukan bagian mana dari TEXTkolom yang ingin Anda indeks.

InnoDBmemiliki batasan 768byte per kunci indeks dan Anda tidak akan dapat membuat indeks lebih lama dari itu.

Ini akan bekerja dengan baik:

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

Perhatikan bahwa nilai maksimum ukuran kunci tergantung pada charset kolom. Ini 767karakter untuk charset byte tunggal seperti LATIN1dan hanya 255karakter untuk UTF8( MySQLhanya menggunakan BMPyang membutuhkan paling banyak 3byte per karakter)

Jika Anda membutuhkan seluruh kolom Anda menjadi PRIMARY KEY, hitung SHA1atau MD5hash dan gunakan sebagai PRIMARY KEY.

Quassnoi
sumber
Persis apa yang saya cari. Terima kasih!
Jonathon Hill
Apakah ukuran (255 di sini) benar-benar dalam karakter dan bukan dalam byte? Karena saya dapat membayangkan bahwa menggunakan karakter untuk UTF-8 akan sangat rumit tanpa manfaat tambahan.
Alexis Wilke
Maaf, saya tidak mengikuti pertanyaan Anda. Dari dokumen: Batas panjang awalan kunci indeks adalah 767 byte untuk tabel InnoDB yang menggunakan format REDUNDANTatau COMPACTbaris. Misalnya, Anda mungkin mencapai batas ini dengan indeks awalan kolom lebih dari 255 karakter pada TEXTatau VARCHARkolom, dengan asumsi set karakter utf8mb3 dan maksimum 3 byte untuk setiap karakter. REDUNDANTdan COMPACTapakah hanya format yang tersedia pada saat jawaban ini diberikan.
Quassnoi
62

Anda bisa menentukan panjang kunci dalam permintaan tabel alter, seperti:

alter table authors ADD UNIQUE(name_first(20), name_second(20));
Mike Evans
sumber
1
Inilah tepatnya yang saya butuhkan untuk memperbaiki masalah yang sama. Terima kasih!
Per Quested Aronsson
2
Anda harus sangat berhati-hati dengan pendekatan ini! Ini mungkin solusi termudah, tetapi dalam banyak situasi bukan yang terbaik. Kunci Anda terdiri dari dua kolom dan urutan kolom penting.
MrD
Jawaban terbaik di sini.
George Chalhoub
Ini menyelesaikan masalah saya, terima kasih banyak!
dellair
22

MySQL melarang pengindeksan nilai penuh BLOB, TEXTdan VARCHARkolom panjang karena data yang dikandungnya bisa sangat besar, dan secara implisit indeks DB akan besar, artinya tidak ada manfaat dari indeks.

MySQL mengharuskan Anda menentukan karakter N pertama yang akan diindeks, dan triknya adalah memilih nomor N yang cukup panjang untuk memberikan selektivitas yang baik, tetapi cukup pendek untuk menghemat ruang. Awalan harus cukup panjang untuk membuat indeks hampir berguna jika Anda akan mengindeks seluruh kolom.

Sebelum kita melangkah lebih jauh, mari kita mendefinisikan beberapa istilah penting. Selektivitas indeks adalah rasio total nilai indeks yang berbeda dan jumlah baris . Berikut adalah satu contoh untuk tabel tes:

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

Jika kita hanya mengindeks karakter pertama (N = 1), maka tabel indeks akan terlihat seperti tabel berikut:

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

Dalam hal ini, selektivitas indeks sama dengan IS = 1/3 = 0,33.

Mari kita lihat apa yang akan terjadi jika kita menambah jumlah karakter yang diindeks menjadi dua (N = 2).

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

Dalam skenario ini IS = 2/3 = 0,66 yang berarti kami meningkatkan selektivitas indeks, tetapi kami juga meningkatkan ukuran indeks. Triknya adalah mencari angka minimal N yang akan menghasilkan secara maksimal selektivitas indeks .

Ada dua pendekatan yang dapat Anda lakukan perhitungan untuk tabel database Anda. Saya akan membuat demonstrasi di Internet dump database ini .

Katakanlah kita ingin menambahkan kolom last_name di tabel karyawan ke indeks, dan kami ingin mendefinisikan angka terkecil N yang akan menghasilkan selektivitas indeks terbaik.

Pertama mari kita kenali nama terakhir yang paling sering:

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)

Seperti yang Anda lihat, nama belakang Baba adalah yang paling sering. Sekarang kita akan menemukan awalan last_name paling sering terjadi , dimulai dengan awalan lima huruf.

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

Ada lebih banyak kejadian setiap awalan, yang berarti kita harus meningkatkan angka N hingga nilainya hampir sama seperti pada contoh sebelumnya.

Ini adalah hasil untuk 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    |
+-----+-----------+

Ini adalah hasil untuk 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)

Ini hasil yang sangat bagus. Ini berarti bahwa kita dapat membuat indeks pada kolom last_namedengan mengindeks hanya 10 karakter pertama. Di kolom definisi tabel last_namedidefinisikan sebagai VARCHAR(16), dan ini berarti kita telah menyimpan 6 byte (atau lebih jika ada karakter UTF8 dalam nama belakang) per entri. Dalam tabel ini ada 1637 nilai berbeda dikalikan dengan 6 byte sekitar 9KB, dan bayangkan bagaimana angka ini akan tumbuh jika tabel kita berisi jutaan baris.

Anda dapat membaca cara-cara lain untuk menghitung jumlah N dalam indeks Prefixed posting saya di MySQL .

Tn
sumber
3
Ini belum cukup diperbarui. Saya menemukan ini jauh lebih mudah untuk dipahami daripada jawaban yang diterima
Mawg mengatakan mengembalikan Monica
10

Saya mendapatkan kesalahan ini saat menambahkan indeks ke tabel dengan kolom jenis teks. Anda perlu mendeklarasikan jumlah ukuran yang ingin Anda gunakan untuk setiap jenis teks.

Masukkan jumlah ukuran dalam kurung ()

Jika terlalu banyak byte digunakan, Anda dapat mendeklarasikan ukuran dalam tanda kurung untuk varchar untuk mengurangi jumlah yang digunakan untuk pengindeksan. Ini bahkan jika Anda menyatakan ukuran untuk tipe yang sudah seperti varchar (1000). Anda tidak perlu membuat tabel baru seperti yang dikatakan orang lain.

Menambahkan indeks

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

Menambahkan indeks unik

alter table test add unique index_name(col1(255),col2(255));
sembunyi-sembunyi
sumber
Jawaban paling sederhana yang saya percaya dan langsung bekerja untuk saya. Terima kasih.
Matt Cremeens
7
alter table authors ADD UNIQUE(name_first(767), name_second(767));

CATATAN : 767 adalah jumlah karakter yang dibatasi hingga mana MySQL akan mengindeks kolom saat berurusan dengan indeks gumpalan / teks

Ref: http://dev.mysql.com/doc/refman/5.7/en/innodb-restrictions.html

Abhishek Goel
sumber
4

Cara hebat lainnya untuk menangani hal ini adalah membuat bidang TEXT Anda tanpa kendala unik dan menambahkan bidang VARCHAR saudara yang unik dan berisi intisari (MD5, SHA1, dll.) Dari bidang TEXT. Hitung dan simpan intisari seluruh bidang TEXT saat Anda menyisipkan atau memperbarui bidang TEXT maka Anda memiliki kendala keunikan atas seluruh bidang TEXT (bukan bagian utama) yang dapat dicari dengan cepat.

par
sumber
1
Anda juga harus sangat berhati-hati dengan string "acak" sepenuhnya, seperti yang dihasilkan oleh MD5 (), SHA1 (), atau UUID (). Setiap nilai baru yang Anda hasilkan bersama mereka akan didistribusikan secara sewenang-wenang di ruang yang besar, yang dapat memperlambat INSERT dan beberapa jenis kueri SELECT:
MrD
2
Distribusi MD5, SHA1 melalui data yang tidak berbahaya harus seragam --- untuk itulah hash.
jb.
alangkah baiknya jika Anda bisa memberikan beberapa contoh.
WebComer
3

Tidak memiliki nilai lama sebagai kunci utama. Itu akan menghancurkan kinerja Anda. Lihat manual mysql, bagian 13.6.13 'Penyesuaian dan Pemecahan Masalah InnoDB'.

Alih-alih, minta pengganti int key sebagai primary (dengan auto_increment), dan loong key Anda sebagai UNIQUE sekunder.

Per Lindberg
sumber
2

Tambahkan kolom varChar (255) lain (dengan default sebagai string kosong bukan nol) untuk menahan overflow ketika 255 karakter tidak cukup, dan ubah PK ini untuk menggunakan kedua kolom. Ini kedengarannya tidak seperti skema database yang dirancang dengan baik, dan saya akan merekomendasikan mendapatkan pemodel data untuk melihat apa yang Anda miliki dengan pandangan ke arah refactoring untuk Normalisasi lebih lanjut.

Charles Bretana
sumber
2

Solusi untuk masalah ini adalah bahwa dalam CREATE TABLEpernyataan Anda, Anda dapat menambahkan kendala UNIQUE ( problemtextfield(300) )setelah kolom membuat definisi untuk menentukan keypanjang 300karakter untuk TEXTbidang, misalnya. Maka 300karakter pertama problemtextfield TEXTbidang harus unik, dan perbedaan setelah itu akan diabaikan.

Siapa Dunnit
sumber
1

Juga, jika Anda ingin menggunakan indeks dalam bidang ini, Anda harus menggunakan mesin penyimpanan MyISAM dan tipe indeks FULLTEXT.

Alexander Valinurov
sumber
Anda dapat mempertimbangkan untuk menambahkan penjelasan dan tautan ke dokumentasi.
LeeGee
1

Sejauh ini tidak ada yang menyebutkan ... dengan utf8mb4 yang merupakan 4-byte dan juga dapat menyimpan emotikon (kita seharusnya tidak pernah lagi menggunakan utf8 3-byte) dan kita dapat menghindari kesalahan seperti Incorrect string value: \xF0\x9F\x98\...kita seharusnya tidak menggunakan VARCHAR (255) melainkan VARCHAR ( lebih tepatnya) 191) karena dalam kasus utf8mb4 dan VARCHAR (255) bagian data yang sama disimpan di luar halaman dan Anda tidak dapat membuat indeks untuk kolom VARCHAR (255) tetapi untuk VARCHAR (191) Anda bisa. Itu karena ukuran kolom maksimum yang diindeks adalah 767 byte untuk ROW_FORMAT = COMPACT atau ROW_FORMAT = REDUNDANT.

Untuk format baris yang lebih baru ROW_FORMAT = DYNAMIC atau ROW_FORMAT = COMPRESSED (yang membutuhkan format file yang lebih baru innodb_file_format = Barracuda bukan Antelope yang lebih tua) ukuran kolom yang diindeks maksimum adalah 3072. Ini tersedia karena MySQL> = 5.6.3 ketika innodb_large_prefix = 1 (dinonaktifkan secara default untuk MySQL <= 5.7.6 dan diaktifkan secara default untuk MySQL> = 5.7.7). Jadi dalam hal ini kita dapat menggunakan VARCHAR (768) untuk utf8mb4 (atau VARCHAR (1024) untuk utf8 lama) untuk kolom yang diindeks. Opsi innodb_large_prefix sudah tidak digunakan lagi sejak 5.7.7 karena perilakunya built-in MySQL 8 (dalam versi ini opsi dihapus).

mikep
sumber
0

Anda harus mengubah jenis kolom ke varcharatau integeruntuk pengindeksan.

Manoj Mishra
sumber
Anda dapat mempertimbangkan untuk menambahkan penjelasan dan tautan ke dokumentasi.
LeeGee
0

Buka mysql edit table-> ubah jenis kolom menjadi varchar(45).

Manoj Mishra
sumber
-1

Gunakan seperti ini

@Id
@Column(name = "userEmailId", length=100)
private String userEmailId;
Krishna Das
sumber
Anda dapat mempertimbangkan untuk menambahkan penjelasan dan tautan ke dokumentasi.
LeeGee