Lỗi MySQL: đặc tả khóa không có độ dài khóa


363

Tôi có một bảng với khóa chính là varchar (255). Một số trường hợp đã phát sinh trong đó 255 ký tự là không đủ. Tôi đã thử thay đổi trường thành văn bản, nhưng tôi gặp lỗi sau:

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

Làm thế nào tôi có thể sửa lỗi này?

chỉnh sửa: Tôi cũng nên chỉ ra bảng này có khóa chính tổng hợp có nhiều cột.


9
Một bảng không thể có nhiều khóa chính. Bạn có nghĩa là nó có một khóa chính tổng hợp (bao gồm nhiều hơn một cột) hoặc nó có nhiều UNIQUEkhóa?
Quassnoi

1
Trong trường hợp của tôi vì một số lý do, tôi đã có một loại văn bản cho một cột email thay vì VARCHAR.
Kris

Sử dụng VARCHAR cho chữ và số duy nhất.
JWC ngày

Câu trả lời:


571

Lỗi xảy ra vì MySQL chỉ có thể lập chỉ mục các ký tự N đầu tiên của BLOB hoặc TEXTcột. Vì vậy, Các lỗi chủ yếu xảy ra khi có một kiểu trường / cột TEXThoặc BLOB hoặc những người thuộc về TEXThoặc BLOBloại như TINYBLOB, MEDIUMBLOB, LONGBLOB, TINYTEXT, MEDIUMTEXT, và LONGTEXTrằng bạn cố gắng để thực hiện một khóa chính hoặc chỉ số. Với đầy đủ BLOBhoặc TEXTkhông có giá trị độ dài, MySQL không thể đảm bảo tính duy nhất của cột vì nó có kích thước thay đổi và động. Vì vậy, khi sử dụng BLOBhoặc TEXTgõ làm chỉ mục, giá trị của N phải được cung cấp để MySQL có thể xác định độ dài khóa. Tuy nhiên, MySQL không hỗ trợ giới hạn độ dài khóa trên TEXThoặc BLOB. TEXT(88)chỉ đơn giản là không làm việc.

Lỗi cũng sẽ bật lên khi bạn cố gắng chuyển đổi một cột trong bảng non-TEXTnon-BLOBnhập như VARCHARENUMvào TEXThoặc BLOBgõ, với cột đã được xác định là các ràng buộc hoặc chỉ mục duy nhất. Lệnh SQL Bảng thay đổi sẽ thất bại.

Giải pháp cho vấn đề là xóa TEXThoặc BLOBcột khỏi chỉ mục hoặc ràng buộc duy nhất hoặc đặt trường khác làm khóa chính. Nếu bạn không thể làm điều đó và muốn đặt một giới hạn trên cột TEXThoặc BLOBcột, hãy thử sử dụng VARCHARloại và đặt giới hạn độ dài cho nó. Theo mặc định, VARCHARđược giới hạn tối đa 255 ký tự và giới hạn của nó phải được chỉ định ngầm trong một dấu ngoặc ngay sau khi khai báo, tức là VARCHAR(200)sẽ chỉ giới hạn ở 200 ký tự.

Đôi khi, ngay cả khi bạn không sử dụng TEXThoặc BLOBloại liên quan trong bảng của mình, Lỗi 1170 cũng có thể xuất hiện. Nó xảy ra trong một tình huống như khi bạn chỉ định VARCHARcột là khóa chính, nhưng đặt sai độ dài hoặc kích thước ký tự của nó. VARCHARchỉ có thể chấp nhận lên đến 256 ký tự, vì vậy bất cứ điều gì như VARCHAR(512)sẽ buộc MySQL để tự động chuyển đổi VARCHAR(512)để một SMALLTEXTkiểu dữ liệu, mà sau đó thất bại với lỗi 1170 trên dài khóa nếu cột được sử dụng như khóa chính hoặc chỉ số duy nhất hoặc không duy nhất. Để giải quyết vấn đề này, chỉ định một con số nhỏ hơn 256 làm kích thước cho VARCHARtrường.

Tham khảo: Lỗi MySQL 1170 (42000): Cột BLOB / văn bản được sử dụng trong Đặc tả khóa không có độ dài khóa


13
dev.mysql.com/doc/refman/5.0/en/char.html "Các giá trị trong các cột VARCHAR là các chuỗi có độ dài thay đổi. Độ dài có thể được chỉ định làm giá trị từ 0 đến 255 trước MySQL 5.0.3 và 0 đến 65.535 trong phiên bản 5.0.3 trở lên. Độ dài tối đa hiệu quả của VARCHAR trong MySQL 5.0.3 trở lên phải tuân theo kích thước hàng tối đa (65.535 byte, được chia sẻ giữa tất cả các cột) "
umassthrower

1
Trường hợp bạn nói "MySQL chỉ có thể lập chỉ mục các ký tự N đầu tiên của cột BLOB hoặc văn bản", giá trị của N là gì?
jameshfisher

2
"Khi bạn lập chỉ mục cột BLOB hoặc văn bản, bạn phải chỉ định độ dài tiền tố cho chỉ mục." dev.mysql.com/doc/refman/5.6/en/column-indexes.html
Vinicius Pinto

86

Bạn nên xác định phần đầu nào của TEXTcột bạn muốn lập chỉ mục.

InnoDBcó giới hạn 768byte trên mỗi khóa chỉ mục và bạn sẽ không thể tạo chỉ mục lâu hơn thế.

Điều này sẽ hoạt động tốt:

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

Lưu ý rằng giá trị tối đa của kích thước khóa phụ thuộc vào bộ ký tự cột. Đó là các 767ký tự cho một bộ ký tự byte đơn LATIN1và chỉ các 255ký tự cho UTF8( MySQLchỉ sử dụng BMPyêu cầu tối đa 3byte trên mỗi ký tự)

Nếu bạn cần toàn bộ cột của bạn là PRIMARY KEY, tính toán SHA1hoặc MD5băm và sử dụng nó như một PRIMARY KEY.


Chính xác những gì tôi đang tìm kiếm. Cảm ơn!
Đồi Jonathon

Là kích thước (255 ở đây) thực sự bằng ký tự và không phải bằng byte? Bởi vì tôi có thể tưởng tượng rằng việc sử dụng các ký tự cho UTF-8 sẽ thực sự phức tạp vì không có thêm lợi ích.
Alexis Wilke

Xin lỗi, tôi không làm theo câu hỏi của bạn. Từ các tài liệu: Giới hạn độ dài tiền tố của khóa chỉ mục là 767 byte cho các bảng InnoDB sử dụng định dạng REDUNDANThoặc COMPACThàng. Ví dụ: bạn có thể đạt giới hạn này với chỉ số tiền tố cột gồm hơn 255 ký tự trên một TEXThoặc một VARCHARcột, giả sử bộ ký tự utf8mb3 và tối đa 3 byte cho mỗi ký tự. REDUNDANTCOMPACTlà các định dạng duy nhất có sẵn tại thời điểm câu trả lời này được đưa ra.
Quassnoi

62

Bạn có thể chỉ định độ dài khóa trong yêu cầu bảng thay đổi, đại loại như:

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

1
Đây chính xác là những gì tôi cần để khắc phục vấn đề tương tự. Cảm ơn!
Per Quested Aronsson

2
Bạn nên rất cẩn thận với phương pháp này! Đây có thể là giải pháp dễ nhất, nhưng trong nhiều tình huống không phải là giải pháp tốt nhất. Khóa của bạn bao gồm hai cột và thứ tự cột là quan trọng.
MrD

Câu trả lời tốt nhất ở đây.
George Chalhoub

Điều này giải quyết vấn đề của tôi, cảm ơn bạn rất nhiều!
dellair

22

Không cho MySQL lập chỉ mục một giá trị đầy đủ của BLOB, TEXTvà dài VARCHARcột vì dữ liệu chúng chứa có thể rất lớn, và mặc nhiên chỉ số DB sẽ lớn, có nghĩa là không có lợi ích từ chỉ mục.

MySQL yêu cầu bạn xác định N ký tự đầu tiên được lập chỉ mục và mẹo là chọn một số N đủ dài để có tính chọn lọc tốt, nhưng đủ ngắn để tiết kiệm không gian. Tiền tố phải đủ dài để làm cho chỉ mục gần như hữu ích nếu bạn lập chỉ mục cho toàn bộ cột.

Trước khi chúng ta đi xa hơn hãy để chúng tôi xác định một số điều khoản quan trọng. Độ chọn lọc chỉ mục là tỷ lệ của tổng giá trị được lập chỉ mục riêng biệt và tổng số hàng . Đây là một ví dụ cho bảng thử nghiệm:

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

Nếu chúng ta chỉ lập chỉ mục ký tự đầu tiên (N = 1), thì bảng chỉ mục sẽ trông giống như bảng sau:

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

Trong trường hợp này, độ chọn lọc chỉ số bằng IS = 1/3 = 0,33.

Bây giờ chúng ta hãy xem điều gì sẽ xảy ra nếu chúng ta tăng số lượng ký tự được lập chỉ mục lên hai (N = 2).

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

Trong kịch bản này IS = 2/3 = 0,66 có nghĩa là chúng tôi đã tăng độ chọn lọc chỉ mục, nhưng chúng tôi cũng đã tăng kích thước của chỉ mục. Thủ thuật là tìm số N tối thiểu sẽ dẫn đến độ chọn lọc chỉ số tối đa .

Có hai cách tiếp cận bạn có thể thực hiện tính toán cho bảng cơ sở dữ liệu của mình. Tôi sẽ thực hiện trình diễn trên bãi chứa cơ sở dữ liệu này .

Giả sử chúng tôi muốn thêm cột Last_name trong bảng nhân viên vào chỉ mục và chúng tôi muốn xác định số N nhỏ nhất sẽ tạo ra độ chọn lọc chỉ mục tốt nhất.

Đầu tiên chúng ta hãy xác định tên cuối cùng thường xuyên nhất:

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)

Như bạn có thể thấy, tên cuối cùng Baba là tên thường xuyên nhất. Bây giờ chúng ta sẽ tìm các tiền tố last_name xảy ra thường xuyên nhất , bắt đầu bằng các tiền tố năm chữ cái.

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

Có nhiều lần xuất hiện hơn của mọi tiền tố, có nghĩa là chúng ta phải tăng số N cho đến khi các giá trị gần giống như trong ví dụ trước.

Đây là kết quả cho 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    |
+-----+-----------+

Đây là kết quả cho 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)

Đây là kết quả rất tốt. Điều này có nghĩa là chúng ta có thể tạo chỉ mục trên cột last_namechỉ với 10 ký tự đầu tiên. Trong cột định nghĩa bảng last_nameđược định nghĩa là VARCHAR(16)và điều này có nghĩa là chúng tôi đã lưu 6 byte (hoặc nhiều hơn nếu có các ký tự UTF8 trong tên cuối cùng) cho mỗi mục nhập. Trong bảng này có 1637 giá trị riêng biệt nhân với 6 byte là khoảng 9KB và hãy tưởng tượng con số này sẽ tăng lên như thế nào nếu bảng của chúng tôi chứa hàng triệu hàng.

Bạn có thể đọc các cách tính số N khác trong bài viết của tôi Chỉ mục tiền tố trong MySQL .


3
Điều này đã không được cập nhật đủ. Tôi thấy nó là nhiều dễ hiểu hơn câu trả lời được chấp nhận
Mawg nói Khôi phục Monica

10

Tôi đã gặp lỗi này khi thêm một chỉ mục vào một bảng với các cột kiểu văn bản. Bạn cần khai báo số lượng kích thước bạn muốn sử dụng cho từng loại văn bản.

Đặt số lượng kích thước trong ngoặc đơn ()

Nếu sử dụng quá nhiều byte, bạn có thể khai báo kích thước trong ngoặc cho varchar để giảm số lượng được sử dụng để lập chỉ mục. Điều này là ngay cả khi bạn đã khai báo một kích thước cho một loại đã giống như varchar (1000). Bạn không cần tạo một bảng mới như những người khác đã nói.

Thêm chỉ mục

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

Thêm chỉ mục duy nhất

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

Câu trả lời đơn giản nhất tôi tin và nó đã làm việc cho tôi ngay lập tức. Cảm ơn.
Matt Cremeens


4

Một cách tuyệt vời khác để giải quyết vấn đề này là tạo trường VĂN của bạn mà không bị ràng buộc duy nhất và thêm trường VARCHAR duy nhất và chứa thông báo (MD5, SHA1, v.v.) của trường TEXT. Tính toán và lưu trữ thông báo trên toàn bộ trường văn bản khi bạn chèn hoặc cập nhật trường văn bản sau đó bạn có một ràng buộc duy nhất đối với toàn bộ trường văn bản (chứ không phải là một phần dẫn đầu) có thể được tìm kiếm nhanh chóng.


1
Bạn cũng nên hết sức cẩn thận với các chuỗi ngẫu nhiên hoàn toàn của người Hồi giáo, chẳng hạn như các chuỗi được sản xuất bởi MD5 (), SHA1 () hoặc UUID (). Mỗi giá trị mới mà bạn tạo với chúng sẽ được phân phối theo cách tùy ý trên một không gian rộng, điều này có thể làm chậm INSERT và một số loại truy vấn CHỌN:
MrD

2
Phân phối MD5, SHA1 trên dữ liệu không độc hại phải thống nhất --- đó là những gì băm dành cho.
jb.

sẽ thật tuyệt nếu bạn có thể cung cấp một số ví dụ.
WebCome

3

Không có giá trị dài làm khóa chính. Điều đó sẽ phá hủy hiệu suất của bạn. Xem hướng dẫn sử dụng mysql, phần 13.6.13 'Điều chỉnh và khắc phục sự cố hiệu suất của InnoDB'.

Thay vào đó, có một khóa int thay thế là chính (với auto_increment) và khóa loong của bạn là một UNIQUE thứ cấp.


2

Thêm một cột varChar (255) khác (với mặc định là chuỗi rỗng không null) để giữ tràn khi 255 ký tự không đủ và thay đổi PK này để sử dụng cả hai cột. Tuy nhiên, điều này không giống như một lược đồ cơ sở dữ liệu được thiết kế tốt, và tôi khuyên bạn nên nhờ một người lập mô hình dữ liệu xem xét những gì bạn có với quan điểm hướng tới việc tái cấu trúc nó để chuẩn hóa hơn.


2

Giải pháp cho vấn đề là trong CREATE TABLEcâu lệnh của bạn , bạn có thể thêm ràng buộc UNIQUE ( problemtextfield(300) )sau khi cột tạo định nghĩa để xác định keyđộ dài 300ký tự cho một TEXTtrường, ví dụ. Sau đó, các 300ký tự đầu tiên của problemtextfield TEXTtrường sẽ là duy nhất và bất kỳ sự khác biệt nào sau đó sẽ bị bỏ qua.


1

Ngoài ra, nếu bạn muốn sử dụng chỉ mục trong trường này, bạn nên sử dụng công cụ lưu trữ MyISAM và loại chỉ mục FULLTEXT.


Bạn có thể xem xét thêm một lời giải thích và liên kết đến tài liệu.
LeeGee

1

Không ai đề cập đến nó cho đến nay ... với utf8mb4 là 4 byte và cũng có thể lưu trữ biểu tượng cảm xúc (chúng ta không bao giờ nên sử dụng utf8 3 byte) và chúng ta có thể tránh các lỗi như Incorrect string value: \xF0\x9F\x98\...chúng ta không nên sử dụng VARCHAR (255) mà là VARCHAR ( 255) 191) vì trong trường hợp utf8mb4 và VARCHAR (255) cùng một phần dữ liệu được lưu ngoài trang và bạn không thể tạo chỉ mục cho cột VARCHAR (255) nhưng đối với VARCHAR (191) bạn có thể. Đó là bởi vì kích thước cột được lập chỉ mục tối đa là 767 byte cho ROW_FORMAT = COMPACT hoặc ROW_FORMAT = REDUNDANT.

Đối với các định dạng hàng mới hơn ROW_FORMAT = DYNAMIC hoặc ROW_FORMAT = COMPRESSED (yêu cầu định dạng tệp mới hơn innodb_file_format = Barracuda không phải Antelope cũ hơn) kích thước cột được lập chỉ mục tối đa là 3072. Nó có sẵn vì MySQL> = 5.6.3 khi innodb_l MySQL <= 5.7.6 và được bật theo mặc định cho MySQL> = 5.7.7). Vì vậy, trong trường hợp này, chúng ta có thể sử dụng VARCHAR (768) cho utf8mb4 (hoặc VARCHAR (1024) cho utf8 cũ) cho cột được lập chỉ mục. Tùy chọn innodb_large_prefix không được chấp nhận kể từ 5.7.7 vì hành vi của nó được tích hợp sẵn MySQL 8 (trong phiên bản này là tùy chọn bị xóa).


0

Bạn phải thay đổi loại cột thành varcharhoặc integerđể lập chỉ mục.


Bạn có thể xem xét thêm một lời giải thích và liên kết đến tài liệu.
LeeGee

0

Chuyển đến mysql edit table-> thay đổi loại cột thành varchar(45).


-1

Sử dụng như thế này

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

Bạn có thể xem xét thêm một lời giải thích và liên kết đến tài liệu.
LeeGee
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.