web-dev-qa-db-ja.com

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

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

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

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

編集:私はまた、このテーブルは複数の列を持つ複合主キーを持っていることを指摘すべきです。

318
GSto

MySQLはBLOBまたはTEXTカラムの最初のN文字しかインデックス付けできないため、エラーが発生します。そのため、主キーまたはインデックスを作成しようとするフィールド/列型がTEXTまたはBLOB、あるいはTEXTBLOBTINYBLOBMEDIUMBLOBLONGBLOB、およびTINYTEXTなどのMEDIUMTEXTまたはLONGTEXT型に属する場合、エラーが主に発生します。長さの値を含まない完全なBLOBまたはTEXTでは、可変サイズおよび動的サイズのため、MySQLは列の一意性を保証できません。そのため、BLOBまたはTEXT型をインデックスとして使用する場合は、MySQLがキー長を決定できるようにNの値を指定する必要があります。ただし、MySQLはTEXTまたはBLOBのキー長制限をサポートしていません。 TEXT(88)は単純にうまくいきません。

また、列が既に一意の制約またはインデックスとして定義されている状態で、テーブルの列をnon-TEXTおよびnon-BLOB型(VARCHARおよびENUM)からTEXTまたはBLOB型に変換しようとした場合も、エラーが発生します。テーブル変更SQLコマンドは失敗します。

この問題を解決するには、インデックスまたはユニーク制約からTEXTまたはBLOB列を削除するか、または別のフィールドを主キーとして設定します。それができず、TEXTまたはBLOB列に制限を設定したい場合は、VARCHAR型を使用して長さの制限を設定してください。デフォルトでは、VARCHARは最大255文字までに制限されており、その制限は宣言の直後の括弧内に暗黙的に指定されなければなりません。すなわち、VARCHAR(200)は200文字までに制限します。

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

参照: MySQLエラー1170(42000):キー長なしのキー指定で使用されたBLOB/TEXTカラム

506
OMG Ponies

あなたはTEXTカラムのどの先頭部分にインデックスを付けたいかを定義するべきです。

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

これはうまくいきます:

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

キーサイズの最大値は、列の文字セットによって異なります。 767のようなシングルバイト文字セットの場合はLATIN1文字、255の場合はUTF8文字のみ(MySQLの場合はBMPを使用するため、1文字あたり最大で3バイト必要)

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

76
Quassnoi

テーブル変更要求でキーの長さを指定できます。

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

MySQLはBLOBTEXTおよびlong VARCHARカラムのフル値のインデックス化を許可していません。それらに含まれるデータは巨大になる可能性があり、暗黙のうちにDBインデックスは大きくなるため、インデックスの恩恵を受けないからです。

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をインデックスに追加し、最小のインデックスを定義するとします。 numberNが最良のインデックス選択性を生み出します。

まず、最も頻繁に現れる姓を特定しましょう。

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)

ご覧のとおり、姓Babaが最もよく使われる名前です。今度は、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)

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

あなたは私の記事でNの数を計算する他の方法を読むことができます MySQLのプレフィックスインデックス .

19
MrD
alter table authors ADD UNIQUE(name_first(767), name_second(767));

NOTE767は、blob/textインデックスの処理中にMySQLがカラムにインデックスを付けるまでの文字数制限です。

参照: http://dev.mysql.com/doc/refman/5.7/ja/innodb-restrictions.html

6
Abhishek Goel

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

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

3
Per Lindberg

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

2
par

255文字では足りない場合にオーバーフローを保持するために別のvarChar(255)列(デフォルトではnullではない空の文字列)を追加し、両方の列を使用するようにこのPKを変更します。しかし、これはうまく設計されたデータベーススキーマのようには思えませんが、より多くの正規化のためにリファクタリングすることを視野に入れて、データモデラにあなたが持っているものを見るように勧めます。

1
Charles Bretana

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

サイズの量をかっこ内に入れる()

使用するバイト数が多すぎる場合は、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));
1
stealth

4バイトで、絵文字を格納することもできるutf8mb4(これ以上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 = Antelopeではない必要)の最大インデックス列サイズは3072です。innodb_large_prefix = 1の場合、MySQL> = 5.6.3以降で使用可能ですMySQL <= 5.7.6、MySQLのデフォルトで有効> = 5.7.7)。そのため、この場合はインデックス付きカラムにutf8mb4にVARCHAR(768)(古いutf8にVARCHAR(1024))を使用できます。オプションinnodb_large_prefixは、その動作がMySQL 8に組み込まれているため(このバージョンではオプションが削除されているため)、5.7.7から非推奨になりました。

0
mikep

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

0

この問題を解決するには、CREATE TABLEステートメントで、列作成定義の後に制約UNIQUE ( problemtextfield(300) )を追加して、例えばkeyフィールドに300文字のTEXTの長さを指定します。その場合、problemtextfieldTEXTフィールドの最初の300文字は固有である必要があり、それ以降の相違は無視されます。

0
Who Dunnit

Mysql edit table->列タイプをvarchar(45)に変更します。

0
Manoj Mishra

インデックスを作成するには、列タイプをvarcharまたはintegerに変更する必要があります。

0
Manoj Mishra

こんな感じ

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