MySQLエラー:キー長のないキー指定


363

varchar(255)である主キーを持つテーブルがあります。255文字では不十分な場合もあります。フィールドをテキストに変更しようとしましたが、次のエラーが発生しました。

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

どうすれば修正できますか?

編集:私はまた、このテーブルには複数の列を持つ複合主キーがあることを指摘する必要があります。


9
テーブルに複数の主キーを含めることはできません。複合主キー(つまり、複数の列を含む)があるのUNIQUEですか、それとも複数のキーがあるのですか?
Quassnoi、2009

1
私の場合、何らかの理由で、VARCHARの代わりに、Eメール列にTEXTタイプがありました。
クリス、2017

一意の英数字にはVARCHARを使用します。
JWC 5

回答:


571

エラーが発生するのは、MySQLがBLOBまたはTEXT列の最初のN文字しかインデックスに登録できないためです。主にフィールド/列のタイプがある場合に起こるエラーは、だから、TEXTまたはBLOBまたはに属するTEXTBLOBのようなタイプはTINYBLOBMEDIUMBLOBLONGBLOBTINYTEXTMEDIUMTEXT、とLONGTEXTあなたは主キーまたはインデックスを作成しようとしています。完全BLOBまたはTEXT長さの値がない場合、MySQLは可変で動的なサイズであるため、列の一意性を保証できません。したがって、インデックスとして使用BLOBまたはTEXTタイプする場合、MySQLがキーの長さを決定できるようにNの値を指定する必要があります。しかし、MySQLは上のキーの長さ制限をサポートしていませんTEXTBLOBTEXT(88)単に機能しません。

あなたからテーブル列を変換しようとするとエラーがポップアップ表示されますnon-TEXTし、non-BLOBなどと入力VARCHARし、ENUMTEXTたりBLOB、すでにユニーク制約またはインデックスとして定義されて列で、タイプ。テーブルの変更SQLコマンドは失敗します。

この問題の解決策は、インデックスまたは一意の制約からTEXTまたはBLOB列を削除するか、別のフィールドを主キーとして設定することです。それを行うことができず、TEXTまたはBLOB列にVARCHAR制限を設定する場合は、type を使用して長さに制限を設定してください。デフォルトでVARCHARは、最大255文字に制限されており、その制限は宣言の直後にブラケット内で暗黙的に指定する必要があります。つまりVARCHAR(200)、200文字に制限されます。

場合によっては、テーブルで使用していないTEXTか、BLOB関連する型を使用していても、エラー1170が表示されることがあります。VARCHAR列を主キーとして指定したが、長さや文字サイズを誤って設定した場合などに発生します。VARCHARだけのような何もして、256文字まで受け入れることができVARCHAR(512)、自動変換するために、MySQLを強制するVARCHAR(512)にはSMALLTEXT列が主キーまたは一意または非一意のインデックスとして使用されている場合、その後キーの長さのエラー1170で失敗したデータ型、。この問題を解決するには、VARCHARフィールドのサイズとして256未満の数値を指定します。

参照:MySQLエラー1170(42000):キー長なしでキー仕様で使用されるBLOB / TEXT列


13
dev.mysql.com/doc/refman/5.0/en/char.html "VARCHARカラムの値は可変長の文字列です。長さは、MySQL 5.0.3以前は0〜255、0〜65,535の値として指定できます5.0.3以降のバージョン。MySQL5.0.3以降のVARCHARの有効な最大長は、最大行サイズ(65,535バイト、すべての列で共有)の
影響を受け

1
「MySQLはBLOBまたはTEXTカラムの最初のN文字しかインデックスに登録できない」と言う場合、Nの値は何ですか?
jameshfisher 14

2
「BLOBまたはTEXTカラムにインデックスを作成する場合、インデックスのプレフィックス長を指定する必要があります。」dev.mysql.com/doc/refman/5.6/en/column-indexes.html
Vinicius Pinto

86

TEXTインデックスを作成する列の先頭部分を定義する必要があります。

InnoDB768インデックスキーごとのバイト数の制限があり、それより長いインデックスを作成することはできません。

これはうまくいきます:

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

キーサイズの最大値は列の文字セットによって異なることに注意してください。これ767は、シングルバイト文字セットのような文字であり、の文字LATIN1のみ255ですUTF8(文字ごとに最大でバイトを必要とするMySQL使用のみ)BMP3

列全体をにする必要がある場合はPRIMARY KEY、計算SHA1またはMD5ハッシュしてとして使用しPRIMARY KEYます。


まさに私が探していたもの。ありがとう!
Jonathon Hill

サイズ(ここでは255)は実際にはバイト単位ではなく文字単位ですか?UTF-8に文字を使用することは、追加の利点がないために本当に複雑になると想像できるからです。
Alexis Wilke 2017年

申し訳ありませんが、質問には従いません。ドキュメントから:インデックスキープレフィックス長の制限使用InnoDBテーブルのための767バイトであるREDUNDANTか、COMPACT行フォーマット。たとえば、utf8mb3文字セットと各文字の最大3バイトを想定して、TEXTまたはVARCHAR列で255文字を超える列プレフィックスインデックスでこの制限に達する可能性があります。 REDUNDANTそしてCOMPACTこの答えが与えられた時点で入手可能な唯一の形式でした。
Quassnoi 2017年

62

次のように、テーブルの変更リクエストでキーの長さを指定できます。

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

1
これは、が同じ問題を修正するため必要なものでした。ありがとう!
Quested Aronssonによる

2
このアプローチには十分注意してください。これはおそらく最も簡単な解決策ですが、多くの状況では最良の解決策ではありません。キーは2つの列で構成されており、列の順序が重要です。
MrD、2015年

ここでベストアンサー。
George Chalhoub

これで私の問題は解決しました。ありがとうございました!
dellair

22

MySQLは、の完全な値と長い列にインデックスを付けることを許可しません。これはBLOB、それらに含まれるデータが巨大になる可能性があり、暗黙的にDBインデックスが大きくなるため、インデックスのメリットがないことを意味します。TEXTVARCHAR

MySQLでは、インデックスを付ける最初のN文字を定義する必要があります。コツは、十分な選択性を提供するのに十分な長さで、スペースを節約するのに十分短いNを選択することです。プレフィックスは、インデックスを列全体にインデックス付けした場合と同じくらい役立つように十分な長さにする必要があります。

さらに進む前に、いくつかの重要な用語を定義しましょう。インデックスの選択性、個別のインデックス値の合計と行の総数の比率です。テストテーブルの例を次に示します。

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

最初の文字(N = 1)のみにインデックスを付けると、インデックステーブルは次のテーブルのようになります。

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

この場合、インデックスの選択性はIS = 1/3 = 0.33と等しくなります。

インデックス付き文字の数を2(N = 2)に増やすとどうなるかを見てみましょう。

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

このシナリオではIS = 2/3 = 0.66です。これは、インデックスの選択性を高めたことを意味しますが、インデックスのサイズも増やしました。トリックは、最大のインデックス選択性をもたらす最小数Nを見つけることです。

データベーステーブルの計算には2つの方法があります。このデータベースダンプのデモを行います

テーブルemployeesのlast_nameをインデックスに追加し、インデックスの選択性が最も高くなる最小の数Nを定義するとします。

最初に、最も頻繁に使用する姓を特定しましょう。

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)

ご覧のように、姓のババが最も頻繁に使用されます。次に、5文字のプレフィックスで始まる、最も頻繁に発生するlast_nameプレフィックスを見つけます。

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

すべての接頭辞の出現回数がはるかに多いため、値が前の例とほぼ同じになるまで、番号Nを増やす必要があります。

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

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)

これは非常に良い結果です。これは、last_name最初の10文字のみにインデックスを付けて、列にインデックスを作成できることを意味します。テーブル定義では、列last_nameはとして定義されています。VARCHAR(16)これは、エントリごとに6バイト(姓にUTF8文字がある場合はそれ以上)を節約したことを意味します。この表には、6バイトを掛けた1637の異なる値があり、約9KBです。テーブルに数百万の行が含まれている場合、この数がどのように増えるかを想像してください。

Nの数を計算する他の方法については、MySQLのポストプレフィックスインデックスを参照してください


3
これは十分に更新されていません。私はあることが判明ずっと受け入れ答えよりも理解しやすい
Mawgはモニカ回復言う

10

テキストタイプの列を持つテーブルにインデックスを追加すると、このエラーが発生しました。各テキストタイプに使用するサイズを宣言する必要があります。

括弧内にサイズ量を入れてください()

使用するバイト数が多すぎる場合は、varcharのかっこ内でサイズを宣言して、インデックス作成に使用する量を減らすことができます。これは、varchar(1000)のような型のサイズを宣言した場合でも同じです。他のユーザーが言っているように、新しいテーブルを作成する必要はありません。

インデックスを追加する

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

一意のインデックスを追加する

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

私が信じる最も簡単な答えは、すぐにうまくいきました。ありがとう。
Matt Cremeens


4

これに対処するもう1つの優れた方法は、一意の制約なしでTEXTフィールドを作成し、一意で、TEXTフィールドのダイジェスト(MD5、SHA1など)を含む兄弟VARCHARフィールドを追加することです。TEXTフィールドを挿入または更新するときに、TEXTフィールド全体のダイジェストを計算して保存すると、すばやく検索できる(一部の先行部分ではなく)テキストフィールド全体に一意性制約が適用されます。


1
また、MD5()、SHA1()、UUID()によって生成される文字列など、完全に「ランダム」な文字列については十分に注意する必要があります。あなたも一緒に生成するそれぞれの新しい値は、INSERTとSELECTクエリのいくつかのタイプを遅らせることができ、大きなスペース、上の任意の方法で配布されます:
MRD

2
悪意のないデータに対するMD5、SHA1の分布は均一でなければなりません---それがハッシュの目的です。
jb。

あなたがいくつかの例を提供できればそれは素晴らしいでしょう。
WebComer 2018年

3

主キーとして長い値を使用しないでください。それはあなたのパフォーマンスを破壊します。mysqlマニュアルのセクション13.6.13「InnoDBパフォーマンスのチューニングとトラブルシューティング」を参照してください。

代わりに、(auto_incrementを使用して)プライマリとして代理のintキーを、セカンダリユニークとしてloongキーを使用します。


2

別のvarChar(255)列(デフォルトではnullではなく空の文字列)を追加して、255文字では不十分なときにオーバーフローを保持し、このPKを変更して両方の列を使用するようにします。これは、適切に設計されたデータベーススキーマのようには聞こえません。データモデラーに、正規化をさらに行うためにリファクタリングするために、自分が持っているものを見てもらうことをお勧めします。


2

問題の解決策は、CREATE TABLEステートメントでUNIQUE ( problemtextfield(300) )、列の定義の後に制約を追加して、たとえばフィールドkey300文字長を指定することTEXTです。その場合300problemtextfield TEXTフィールドの最初の文字は一意である必要があり、その後の違いは無視されます。


1

また、このフィールドでインデックスを使用する場合は、MyISAMストレージエンジンとFULLTEXTインデックスタイプを使用する必要があります。


説明とドキュメントへのリンクを追加することを検討してください。
LeeGee

1

これまでのところ誰も言及していません... utf8mb4は4バイトで、絵文字も格納できます(3バイトのutf8はこれ以上使用しIncorrect string value: \xF0\x9F\x98\...ないでください)。通常のVARCHAR(255)ではなくVARCHAR( 191) utf8mb4とVARCHAR(255)の場合、データの同じ部分がページ外に格納され、列VARCHAR(255)にはインデックスを作成できませんが、VARCHAR(191)には作成できるためです。これは、ROW_FORMAT = COMPACTまたはROW_FORMAT = REDUNDANTの最大インデックス列サイズが767バイトであるためです。

新しい行フォーマットの場合、ROW_FORMAT = DYNAMICまたはROW_FORMAT = COMPRESSED(新しいファイルフォーマットinnodb_file_format = Barracudaではなく、Antelopeが必要)の最大インデックス列サイズは3072です。これは、MySQL> = 5.6.3以降、innodb_large_prefix = 1の場合に使用できます(デフォルトでは無効になっています) MySQL <= 5.7.6、MySQLのデフォルトで有効> = 5.7.7)。したがって、この場合、インデックス付きの列にutf8mb4にはVARCHAR(768)(古いutf8にはVARCHAR(1024))を使用できます。オプションinnodb_large_prefixは、その動作が組み込みのMySQL 8(このバージョンではオプションが削除されている)であるため、5.7.7以降非推奨です。


0

列タイプを索引付けに変更するvarcharinteger、索引付けする必要があります。


説明とドキュメントへのリンクを追加することを検討してください。
LeeGee


-1

このように使う

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

説明とドキュメントへのリンクを追加することを検討してください。
LeeGee
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.