MySQL hatası: anahtar uzunluğu olmayan anahtar özellikleri


363

Bir varchar (255) birincil anahtar içeren bir tablo var. 255 karakterin yeterli olmadığı bazı durumlar ortaya çıkmıştır. Alanı bir metne değiştirmeyi denedim, ancak aşağıdaki hatayı alıyorum:

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

bunu nasıl düzeltebilirim?

edit: Ayrıca bu tablonun birden çok sütun ile bileşik bir birincil anahtar olduğunu belirtmek gerekir.


9
Bir tablonun birden fazla birincil anahtarı olamaz. Bileşik bir birincil anahtara (birden fazla sütun içeren) veya birden fazla UNIQUEanahtara sahip olduğu anlamına mı geliyor?
Quassnoi

1
Benim durumumda nedense VARCHAR yerine bir e-posta sütunu için bir METİN türü vardı.
Kris

Benzersiz alfasayısal için VARCHAR kullanın.
JWC

Yanıtlar:


571

Hata, MySQL'in yalnızca bir BLOB veya TEXTsütunun ilk N karakterlerini dizine ekleyebilmesi nedeniyle oluşur . Tarlası / sütun tipi olduğunda hata esas olur Yani TEXTDAMLA ya aittir olanlar ya TEXTya BLOBgibi türleri TINYBLOB, MEDIUMBLOB, LONGBLOB, TINYTEXT, MEDIUMTEXT, ve LONGTEXTbir birincil anahtar veya dizin yapmaya söyledi. Tam BLOBveya TEXTuzunluk değeri olmadan, MySQL değişken ve dinamik boyutta olduğu için sütunun benzersizliğini garanti edemez. Bu nedenle, BLOBveya TEXTdizin olarak türleri kullanırken , MySQL'in anahtar uzunluğunu belirleyebilmesi için N değeri sağlanmalıdır. Ancak, MySQL TEXTveya üzerinde anahtar uzunluk sınırını desteklemez BLOB. TEXT(88)sadece işe yaramaz.

Eğer bir tablo sütunu dönüştürmeye çalıştığınızda hata da açılır non-TEXTve non-BLOBörneğin yazın VARCHARve ENUMiçine TEXTveya BLOBzaten benzersiz kısıtlamaları veya endeks olarak tanımlanmıştır sütun ile yazın. Alter Table SQL komutu başarısız olur.

Sorunun çözümü, TEXTveya BLOBsütunu dizinden veya benzersiz kısıtlamadan kaldırmak veya başka bir alanı birincil anahtar olarak ayarlamaktır. Bunu yapamıyorsanız ve TEXTveya BLOBsütuna bir sınır koymak istiyorsanız, VARCHARtürü kullanmayı deneyin ve üzerine bir uzunluk sınırı koyun. Varsayılan olarak, VARCHARen fazla 255 karakterle sınırlıdır ve sınırı, bildirilmesinden hemen sonra bir parantez içinde dolaylı olarak belirtilmelidir, yani VARCHAR(200)yalnızca 200 karakter uzunluğunda sınırlanacaktır.

Bazen, tablonuzda TEXTveya BLOBilişkili türü kullanmasanız da , Hata 1170 de görünebilir. VARCHARSütunu birincil anahtar olarak belirttiğiniz , ancak uzunluğunu veya karakter boyutunu yanlış ayarladığınız gibi bir durumda gerçekleşir . VARCHARyalnızca 256 karaktere kadar kabul edebilir, bu nedenle VARCHAR(512)MySQL gibi herhangi bir şey , sütun birincil anahtar veya benzersiz veya benzersiz olmayan dizin olarak kullanılırsa, anahtar uzunluğunda 1170 hatasıyla başarısız olan VARCHAR(512)bir SMALLTEXTveri türüne otomatik olarak dönüştürmeye zorlar . Bu sorunu çözmek için VARCHARalan boyutu olarak 256'dan küçük bir rakam belirtin .

Başvuru: MySQL Hatası 1170 (42000): Anahtar Uzunluğunda Anahtar Özelliğinde Kullanılan BLOB / TEXT Sütunu


13
dev.mysql.com/doc/refman/5.0/tr/char.html "VARCHAR sütunlarındaki değerler değişken uzunluklu dizelerdir. Uzunluk, MySQL 5.0.3'ten önce 0 ile 255 ve 0 ile 65.535 arasında bir değer olarak belirtilebilir MySQL 5.0.3 ve sonraki sürümlerinde bir VARCHAR'ın etkin maksimum uzunluğu, maksimum satır boyutuna tabidir (tüm sütunlar arasında paylaşılan 65.535 bayt) "
umassthrower

1
"MySQL, bir BLOB veya METİN sütununun yalnızca ilk N grafiğini dizine ekleyebilir" dediğinizde, N'nin değeri nedir?
jameshfisher

2
"Bir BLOB veya METİN sütununu dizine eklerken, dizin için bir önek uzunluğu belirtmelisiniz." dev.mysql.com/doc/refman/5.6/tr/column-indexes.html
Vinicius Pinto

86

Bir TEXTsütunun hangi önde gelen bölümünü dizine eklemek istediğinizi tanımlamanız gerekir .

InnoDB768dizin anahtarı başına bayt sınırlaması vardır ve bundan daha uzun bir dizin oluşturamazsınız.

Bu iyi çalışır:

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

Anahtar boyutunun maksimum değerinin sütun karakter kümesine bağlı olduğunu unutmayın. Bu var 767gibi tek baytlık karakter kümesi için karakterler LATIN1ve sadece 255için karakterler UTF8( MySQLyalnızca kullandığı BMPen fazla gerektirir 3karakter başına bayt)

Tüm sütununuzun olması gerekiyorsa PRIMARY KEY, hesaplayın SHA1veya MD5karma yapın ve bir olarak kullanın PRIMARY KEY.


Tam aradığım şey. Teşekkürler!
Jonathon Hill

Boyut (burada 255) bayt cinsinden değil, gerçekten karakter cinsinden mi? Çünkü UTF-8 için karakter kullanmanın ek bir fayda olmadan gerçekten karmaşık olacağını hayal edebiliyorum.
Alexis Wilke

Üzgünüm, sorunuzu takip etmiyorum. Dokümanlardan: veya satır biçimini kullanan InnoDB tabloları için dizin anahtarı önek uzunluğu sınırı 767 bayttır . Örneğin, bir utf8mb3 karakter kümesi ve her karakter için maksimum 3 bayt olduğu varsayılarak , bir veya sütun üzerinde 255 karakterden fazla bir sütun önek diziniyle bu sınıra ulaşabilirsiniz. REDUNDANTCOMPACTTEXTVARCHAR REDUNDANTve COMPACTbu cevabın verildiği tarihte mevcut olan tek formatlardı.
Quassnoi

62

Değişiklik tablosu isteğinde anahtar uzunluğunu aşağıdaki gibi belirtebilirsiniz:

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

1
Bu tam olarak ne olduğunu ben aynı sorunu çözmek gerekiyordu. Teşekkürler!
Görev Aradı Aronsson

2
Bu yaklaşıma çok dikkat etmelisiniz! Bu belki de en kolay çözümdür, ancak birçok durumda en iyisi değildir. Anahtarınız iki sütundan oluşur ve sütun sırası önemlidir.
MrD

En iyi cevap burada.
George Chalhoub

22

MySQL BLOB, içerdikleri veriler çok büyük olabileceğinden ve dolaylı olarak DB dizini büyük olacağından, dizinten fayda sağlamayacağı için tam bir değer TEXTve uzun VARCHARsütunların dizine eklenmesine izin vermez .

MySQL, dizine eklenecek ilk N karakterleri tanımlamanızı gerektirir ve hile, iyi seçicilik sağlayacak kadar uzun, ancak yerden tasarruf edecek kadar kısa bir N sayısı seçmektir. Ön ek, dizini, tüm sütunu dizine eklediğinizde olabildiğince kullanışlı hale getirecek kadar uzun olmalıdır.

Daha ileri gitmeden önce bazı önemli terimleri tanımlayalım. Dizin seçiciliği , toplam farklı dizinlenmiş değerlerin ve toplam satır sayısının oranıdır . Test tablosu için bir örnek:

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

Yalnızca ilk karakteri dizine alırsak (N = 1), dizin tablosu aşağıdaki tabloya benzer:

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

Bu durumda, dizin seçiciliği IS = 1/3 = 0.33'e eşittir.

Şimdi dizine eklenen karakter sayısını ikiye (N = 2) çıkarırsak ne olacağını görelim.

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

Bu senaryoda IS = 2/3 = 0.66, yani endeks seçiciliğini artırdık, fakat endeks boyutunu da arttırdık. Hile, maksimum dizin seçiciliği ile sonuçlanacak minimum N sayısını bulmaktır .

Veritabanı tablonuz için hesaplamalar yapabileceğiniz iki yaklaşım vardır. Bu veritabanı dökümü üzerinde gösteri yapacak .

Diyelim ki tablo çalışanlarına last_name sütununu dizine eklemek istiyoruz ve en iyi dizin seçiciliğini sağlayacak en küçük N sayısını tanımlamak istiyoruz .

İlk önce en sık kullanılan soyadları tanımlayalım:

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)

Gördüğünüz gibi, soyadı Baba en sık kullanılanı. Şimdi beş harfli öneklerden başlayarak en sık karşılaşılan soyadı öneklerini bulacağız .

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

Her önekin çok daha fazla örneği vardır, bu da değerler önceki örnekteki ile hemen hemen aynı olana kadar N sayısını artırmamız gerektiği anlamına gelir.

İşte N = 9 için sonuçlar

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

İşte N = 10 için sonuçlar.

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

Bu çok iyi sonuçlar. Bu, last_nameyalnızca ilk 10 karakteri dizine ekleyerek sütunda dizin oluşturabileceğimiz anlamına gelir . Tablo tanımı sütununda last_nameolarak tanımlanır VARCHAR(16)ve bu, giriş başına 6 bayt (veya soyadında UTF8 karakterleri varsa daha fazla) kaydettiğimiz anlamına gelir. Bu tabloda, 6 bayt ile çarpılan yaklaşık 1637 farklı değer vardır ve yaklaşık 9KB'dir ve tablonuzda milyon satır varsa bu sayının nasıl büyüyeceğini hayal edin.

Sen sayısını hesaplarken başka yollarını okuyabilir N mesajımın içinde MySQL öneki endeksler .


3
Bu yeterince güncellenmedi.
Anlaşmayı

11

Metin türü sütun içeren bir tabloya bir dizin eklerken bu hatayı aldım. Her metin türü için kullanmak istediğiniz boyut miktarını bildirmeniz gerekir.

Boyut miktarını parantez içine alın ()

Çok fazla bayt kullanılırsa, indeksleme için kullanılan miktarı azaltmak amacıyla varchar için parantez içinde bir boyut bildirebilirsiniz. Bu zaten varchar (1000) gibi bir tür için bir boyut bildirmiş olsanız bile. Diğerlerinin söylediği gibi yeni bir tablo oluşturmanız gerekmez.

Dizin ekleme

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

Benzersiz dizin ekleme

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

En basit cevap inanıyorum ve hemen benim için çalıştı. Teşekkürler.
Matt Cremeens


4

Bununla başa çıkmanın bir başka mükemmel yolu, TEXT alanınızı benzersiz kısıtlama olmadan oluşturmak ve TEXT alanının bir özetini (MD5, SHA1 vb.) İçeren bir kardeş VARCHAR alanı eklemektir. METİN alanını eklediğinizde veya güncellediğinizde özeti tüm METİN alanı üzerinde hesaplayın ve saklayın, ardından hızlı bir şekilde aranabilen tüm METİN alanı (bazı önde gelen kısımlar yerine) üzerinde benzersiz bir kısıtlama vardır.


1
MD5 (), SHA1 () veya UUID () tarafından üretilenler gibi tamamen “rastgele” dizelere de çok dikkat etmelisiniz. Onlarla oluşturduğunuz her yeni değer, INSERT ve bazı SELECT sorgu türlerini yavaşlatabilecek geniş bir alana rastgele şekilde dağıtılacaktır:
MrD

2
MD5, SHA1'in kötü amaçlı olmayan verilere göre dağılımı tekdüze olmalıdır --- karmaların amacı budur.
jb.

bazı örnek verebilirseniz harika olur.
WebComer

3

Birincil anahtar olarak uzun değerlere sahip olmayın. Bu performansınızı yok edecektir. Bkz. Mysql el kitabı, bölüm 13.6.13 'InnoDB Performans Ayarı ve Sorun Giderme'.

Bunun yerine, birincil (auto_increment ile) bir yedek int anahtarına ve ikincil UNIQUE olarak uzun anahtarınıza sahip olun.


2

255 karakter yeterli olmadığında taşmayı tutmak için başka bir varChar (255) sütunu (varsayılan olarak boş dize boş değil) ekleyin ve bu PK'yi her iki sütunu da kullanacak şekilde değiştirin. Bu iyi tasarlanmış bir veritabanı şeması gibi görünmüyor ve daha Normalizasyon için yeniden düzenleme doğru bir görünüm ile ne bakmak için bir veri modelleyici almanızı tavsiye ederim.


2

Sorunun çözümü CREATE TABLE, ifadenizde , örneğin bir alan için bir karakter uzunluğu UNIQUE ( problemtextfield(300) )belirtmek için sütun tanımları oluşturduktan sonra kısıtlamayı ekleyebilirsiniz . Daha sonra alanın ilk karakterlerinin benzersiz olması gerekir ve bundan sonraki farklar göz ardı edilir.key300TEXT300problemtextfield TEXT


1

Ayrıca, bu alanda dizin kullanmak istiyorsanız, MyISAM depolama motorunu ve FULLTEXT dizin türünü kullanmalısınız.


Bir açıklama ve belgelere bağlantı eklemeyi düşünebilirsiniz.
LeeGee

1

Kimse şu ana kadar bahsetmedi ... 4 baytlık ve aynı zamanda ifadeleri saklayabilen utf8mb4 ile (asla 3 bayt utf8 kullanmamalıyız) ve Incorrect string value: \xF0\x9F\x98\...tipik VARCHAR (255) yerine daha ziyade VARCHAR ( 191) çünkü utf8mb4 ve VARCHAR (255) verilerin aynı kısmı sayfa dışında saklanır ve VARCHAR (255) sütunu için dizin oluşturamazsınız, ancak VARCHAR (191) için dizin oluşturamazsınız. Bunun nedeni, dizinlenmiş maksimum sütun boyutunun ROW_FORMAT = COMPACT veya ROW_FORMAT = REDUNDANT için 767 bayt olmasıdır.

Daha yeni satır formatları için ROW_FORMAT = DYNAMIC veya ROW_FORMAT = COMPRESSED (daha yeni dosya formatı gerektiren innodb_file_format = Barracuda eski değil Antilop) maksimum dizinli sütun boyutu 3072'dir. MySQL> = 5.6.3'ten beri innodb_large_prefix = 1 (varsayılan olarak devre dışı MySQL <= 5.7.6 ve MySQL> = 5.7.7 için varsayılan olarak etkindir). Bu durumda, endekslenmiş sütun için utf8mb4 için VARCHAR (768) (veya eski utf8 için VARCHAR (1024)) kullanabiliriz. Davranış yerleşik MySQL 8 (bu sürümde seçenek kaldırılmıştır) olduğundan, innodb_large_prefix seçeneği 5.7.7'den beri kullanımdan kaldırılmıştır.


0

İndeksleme için sütun türünü varcharveya integerdizinini değiştirmeniz gerekir .


Bir açıklama ve belgelere bağlantı eklemeyi düşünebilirsiniz.
LeeGee

0

Mysql edit table-> sütun türünü değiştir seçeneğine gidin varchar(45).


-1

Bunun gibi kullan

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

Bir açıklama ve belgelere bağlantı eklemeyi düşünebilirsiniz.
LeeGee
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.