web-dev-qa-db-ja.com

#1071 - 指定されたキーが長すぎました。最大キー長は767バイトです

次のコマンドを実行したとき

ALTER TABLE `mytable` ADD UNIQUE (
`column1` ,
`column2`
);

私はこのエラーメッセージを得ました:

#1071 - Specified key was too long; max key length is 767 bytes

Column1とcolumn2に関する情報:

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci

varchar(20)に必要なのは21バイトだけで、varchar(500)に必要なのは501バイトだけです。したがって、合計バイト数は522で、767未満です。エラーメッセージが表示されたのはなぜですか?

#1071 - Specified key was too long; max key length is 767 bytes
451
Steven

767バイトは、MySQLバージョン5.6(およびそれ以前のバージョン)のInnoDBテーブルの 記載されているプレフィックス制限 です。 MyISAMテーブルの場合は1,000バイトです。 MySQLバージョン5.7以降では、この制限は3072バイトに増えました。

また、utf8mb4でエンコードされたbig charまたはvarcharフィールドにインデックスを設定する場合、最大インデックスプレフィックス長767バイト(または3072バイト)を4で割ると191になることにも注意する必要があります。 utf8mb4文字の最大長は4バイトです。 UTF-8文字の場合、3バイトになり、最大インデックスプレフィックス長は254になります。

1つの選択肢は、VARCHARフィールドに下限を設定することです。

もう1つのオプション( この問題への対応 )によると、全体の金額ではなく列のサブセットを取得することができます。

ALTER TABLE `mytable` ADD UNIQUE ( column1(15), column2(200) );

適用するための鍵を取得する必要があるので微調整しますが、このエンティティに関するデータモデルを確認して、MySQLの制限を超えずに目的のビジネスルールを実装できるようにする改善があるかどうかを検討する価値があるでしょうか。

388
OMG Ponies

INNODB/Utf-8でVARCHAR(256)フィールドにUNIQUEインデックスを付けようとしている人に問題がある場合は、VARCHAR(255)に切り替えてください。 255が制限のようです。

380
PinkTurtle

あなたが限界に達すると。以下を設定してください。

  • INNODB utf8VARCHAR(255)
  • INNODB utf8mb4VARCHAR(191)
271
Aley

MySQLは、文字列の1文字あたりのバイト数が最悪の場合を想定しています。 MySQLの 'utf8'エンコーディングでは、そのエンコーディングはU+FFFFを超える文字を許可しないため、1文字あたり3バイトです。 MySQLの「utf8mb4」エンコーディングの場合、これは1文字あたり4バイトです。これは、MySQLが実際のUTF-8と呼ぶものだからです。

ですから、あなたが 'utf8'を使っていると仮定すると、あなたの最初のカラムはインデックスの60バイトを取り、そしてあなたのもう二つは1500を取ります。

144
morganwahl

クエリの前にこのクエリを実行します。

SET @@global.innodb_large_prefix = 1;

これは3072 bytesの上限を増やすでしょう。

44
Raza Ahmed

どの文字エンコーディングを使用していますかUTF-16などの一部の文字セットは、1文字に複数のバイトを使用します。

37
Amber

Laravel Frameworkの解決策

Laravel 5.4。*ドキュメントのとおり ;以下のように、app/Providers/AppServiceProvider.phpファイルのbootメソッド内でデフォルトの文字列長を設定する必要があります。

use Illuminate\Support\Facades\Schema;

public function boot() 
{
    Schema::defaultStringLength(191); 
}

この修正についての説明、 Laravel 5.4。* documentation

Laravelはデフォルトでutf8mb4文字セットを使用します。これはデータベースに "emojis"を格納するためのサポートを含みます。バージョン5.7.7より前のMySQLまたはバージョン10.2.2より前のMariaDBを実行している場合は、MySQLでインデックスを作成するために、移行によって生成されるデフォルトの文字列長を手動で設定する必要があります。 AppServiceProviderの中でSchema::defaultStringLengthメソッドを呼び出すことでこれを設定できます。

あるいは、データベースに対してinnodb_large_prefixオプションを有効にすることもできます。このオプションを正しく有効にする方法については、データベースのマニュアルを参照してください。

31
alishaukat

Varchar(500)は501バイトしか必要としないのに対し、varchar(20)は21バイトしか必要としないと思います。したがって、合計バイト数は522で、767未満です。エラーメッセージが表示されたのはなぜですか?

そのため、文字列を格納するにはUTF8に1文字あたり3バイトが必要ですなので、20 + 500文字= 20 * 3 + 500 * 3 = 1560 bytes = 以上許可767バイト。

UTF8の制限は767/3 = 255文字です。1文字あたり4バイトを使用するUTF8mb4の場合、767/4 = 191文字です。


制限よりも長い列を使用する必要がある場合、この問題には2つの解決策があります。

  1. "安い"エンコーディング(1文字あたりのバイト数が少なくて済むエンコーディング)を使用 _
    私の場合は、SEOの記事の文字列を含む列に一意のインデックスを追加する必要がありました。SEOには[A-z0-9\-]文字のみを使用し、1文字あたり1バイトのみを使用するlatin1_general_ciを使用し、列の長さは767バイトにできるようにしました。
  2. あなたのコラムからハッシュを作成し、それだけでユニークインデックスを使う
    私にとってもう1つの選択肢は、SEOのハッシュを格納する列をもう1つ作成することでした。この列には、SEO値が一意であることを保証するためのUNIQUEキーがあります。また、検索速度を上げるために、元のSEO列にKEYインデックスを追加します。
22
Buksy

なぜエラーメッセージが表示されるのかについての回答は、ここですでに多くのユーザーによって回答されています。私の答えは、それをどのように修正して使用するかということです。

このリンクから を参照。

  1. MySQLクライアント(またはMariaDBクライアント)を開きます。これはコマンドラインツールです。
  2. それはあなたのパスワードを尋ねるでしょう、あなたの正しいパスワードを入力してください。
  3. このコマンドを使用してデータベースを選択してくださいuse my_database_name;

データベースが変更されました

  1. set global innodb_large_prefix=on;

クエリOK、影響を受けた0行(0.00秒)

  1. set global innodb_file_format=Barracuda;

クエリOK、影響を受けた0行(0.02秒)

  1. 簡単な管理のためにphpMyAdminまたはそのようなものでデータベースにアクセスしてください。 >データベース>テーブルの表示 構造 > 操作 タブに移動します。 > ROW_FORMAT _ dynamic _ に変更して、変更を保存します。
  2. テーブルの structure tab> Unique をクリックしてください。
  3. 完了しました。今すぐエラーがありません。

この修正の問題は、dbを別のサーバにエクスポートする場合(たとえばlocalhostから実際のHostへ)、そのサーバでMySQLコマンドラインを使用できない場合です。あなたはそれをそこで動作させることはできません。

18
vee
Specified key was too long; max key length is 767 bytes

latin-1文字セットを使用した場合にのみ、1バイトが1文字に相当するため、このメッセージが表示されました。 utf8を使用した場合、キー列を定義するときに各文字は3バイトと見なされます。 utf8mb4を使用すると、キー列を定義するときに各文字は4バイトと見なされます。したがって、キーフィールドが許可しようとしているバイト数を決定するには、キーフィールドの文字数制限に1、3、または4を掛けます(この例では)。 uft8mb4を使用している場合、ネイティブのInnoDBの主キーフィールドには191文字しか定義できません。 767バイトを破らないでください。

17

あなたは長い列のmd5の列を追加することができます

15
diyism

Utf8mb4を使用してVARCHAR(255)フィールドにUNIQUEインデックスを追加しようとしたときに、この問題が発生しました。この問題はすでにここで概説されていますが、私たちがこれをどのように考え出し解決したかについての実際的なアドバイスを追加したいと思いました。

Utf8mb4を使用するとき、文字は4バイトとして数えますが、utf8の下では、それらは3バイトとすることができます。 InnoDBデータベースはインデックスが767バイトしか含むことができないという制限があります。そのため、utf8を使用する場合は255文字(767/3 = 255)を格納できますが、utf8mb4を使用する場合は191文字(767/4 = 191)しか格納できません。

Utf8mb4を使用してVARCHAR(255)フィールドに通常のインデックスを追加することは絶対に可能ですが、インデックスサイズは自動的に191文字に切り捨てられます - ここでunique_keyのように:

Sequel Pro screenshot showing index truncated at 191 characters

通常のインデックスはMySQLがデータをより迅速に検索するのを助けるために使用されるだけなので、これは問題ありません。フィールド全体を索引付けする必要はありません。

では、なぜMySQLは通常のインデックスに対して自動的にインデックスを切り捨てますが、ユニークインデックスに対してそれを行おうとすると明示的なエラーを投げますか? MySQLが挿入または更新される値がすでに存在するかどうかを判断できるようにするには、その一部ではなく実際に値全体にインデックスを付ける必要があります。

一日の終わりに、フィールドに一意のインデックスを付けたい場合は、フィールドの内容全体がインデックスに収まる必要があります。 utf8mb4の場合、これはVARCHARフィールドの長さを191文字以下に減らすことを意味します。そのテーブルまたはフィールドにutf8mb4が必要ない場合は、utf8にドロップして、255個の長さのフィールドを保持することができます。

11
maknz

これが私の最初の答えです。

データベースを削除してこのように再作成するだけで、エラーはなくなります。

drop database if exists rhodes; create database rhodes default CHARACTER set utf8 default COLLATE utf8_general_ci;

しかし、それはすべての場合にはうまくいきません。

実際には、特定の長さを超える文字数を持つVARCHAR列で、文字セットutf8またはutf8mb4)を持つVARCHAR列に索引を使用することは問題です。 utf8mb4の場合、その特定の長さは191です。

MySQLデータベースでロングインデックスを使用する方法の詳細については、この記事のロングインデックスのセクションを参照してください。データベース文字セットとutf8mb4

8

このトピックを検索したところ、ついにカスタム変更がありました

MySQLワークベンチ6.3.7バージョンの場合グラフィカルインターフェーズが利用可能です

  1. Workbenchを起動して接続を選択します。
  2. 管理またはインスタンスに移動して、オプションファイルを選択します。
  3. Workbenchから設定ファイルの読み取り許可を求められたら、[OK]を2回押して許可します。
  4. 中央の場所に管理者オプションファイルウィンドウが表示されます。
  5. [InnoDB]タブに移動し、[全般]セクションで[innodb_large_prefix]がオンになっていない場合はオンにします。
  6. innodb_default_row_formatオプションの値をDYNAMICに設定します。

6.3.7より下のバージョンでは直接オプションは利用できないのでコマンドプロンプトで行く必要がある

  1. 管理者としてCMDを起動します。
  2. Mysqlサーバーがインストールされているディレクターに行くほとんどの場合は "C:¥Program Files¥MySQL¥MySQL Server 5.7¥bin"なのでコマンドは "cd¥" "cd Program Files¥MySQL¥MySQL Server 5.7¥bin"です。
  3. 今すぐコマンドmysql -u userName -p databasescheemaを実行します今それはそれぞれのユーザーのパスワードを要求しました。パスワードを入力してmysqlプロンプトに入ります。
  4. 以下のコマンドを1つずつ入力して、いくつかのグローバル設定を設定する必要があります。set global innodb_large_prefix = on;グローバルに設定innodb_file_format = barracuda; global innodb_file_per_table = trueを設定します。
  5. 最後に、必要なテーブルのROW_FORMATをデフォルトでそのCOMPACTで変更する必要があります。それをDYNAMICに設定する必要があります。
  6. 次のコマンドを使用します。alter table table_name ROW_FORMAT = DYNAMIC;
  7. 完了
6
Abhishek

5回避策:

制限は5.7.7(MariaDB 10.2.2?)で引き上げられました。そしてそれは5.6(10.1)のいくつかの作業で増やすことができます。

あなたがCHARACTER SET utf8mb4を使おうとしたために限界に当たっているなら。次に、エラーを回避するために次のいずれかを実行します(それぞれに欠点があります)。

⚈  Upgrade to 5.7.7 for 3072 byte limit -- your cloud may not provide this;
⚈  Change 255 to 191 on the VARCHAR -- you lose any values longer than 191 characters (unlikely?);
⚈  ALTER .. CONVERT TO utf8 -- you lose Emoji and some of Chinese;
⚈  Use a "prefix" index -- you lose some of the performance benefits.
⚈  Or... Stay with older version but perform 4 steps to raise the limit to 3072 bytes:

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=1;
SET GLOBAL innodb_large_prefix=1;
logout & login (to get the global values);
ALTER TABLE tbl ROW_FORMAT=DYNAMIC;  -- (or COMPRESSED)

- http://mysql.rjweb.org/doc.php/limits#767_limit_in_innodb_indexes

5
Rick James

私はこの問題を修正しました:

varchar(200) 

交換された

varchar(191)

200を超えるすべてのvarcharは、それらを191に置き換えるかテキストに設定します。

5
flik

照合順序を変更してください。あなたは utf8_general_ci を使うことができます

5
Nids Barthwal

テーブル作成時にutf8mb4utf8に変更するだけで私の問題は解決しました。例えば、CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;からCREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;までです。

3
MajidJafari

Laravel 5.7または5.6の場合

従うべきステップ

  1. App\Providers\AppServiceProvider.phpに行きます。
  2. 一番上のプロバイダuse Illuminate\Support\Facades\Schema;にこれを追加してください。
  3. Boot関数の内側これを追加Schema::defaultStringLength(191);

そのすべて、お楽しみください。

2
Manojkiran.A

以下の列に基づいて、これら2つの可変文字列列はutf8_general_ci照合を使用しています(utf8 charsetが暗黙指定されています)。

MySQLでは、utf8文字セットは各文字に対して最大 3バイト を使用します。したがって、500 * 3 = 1500バイトを割り当てる必要があります。これは、MySQLで許容されている767バイトよりはるかに大きいものです。そのため、この1071エラーが発生しています。

言い換えれば、すべての文字セットがシングルバイト表現であるとは限らないため、文字セットのバイト表現に基づいて文字数を計算する必要があります。 MySQLのutf8は1文字あたり最大3バイト、767/3≒255文字を使用し、utf8mb4では最大4バイト表現、767/4≒191文字を使用します。

MySQLも知られています

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci
2
Devy

インポートファイルでutf8mb4utf8に置き換えます。

enter image description here

2
Bryan

接頭辞の制限により、このエラーが発生します。 767バイトは、5.7より前のMySQLバージョンのInnoDBテーブルに記載されているプレフィックス制限です。 MyISAMテーブルの場合は1,000バイトです。 MySQLバージョン5.7以降では、この制限は3072バイトに増えました。

エラーが発生したサービスで次のコマンドを実行すると、問題は解決します。これはMYSQL CLIで実行する必要があります。

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=on;
SET GLOBAL innodb_large_prefix=on;
1
Rivers

私の場合、Linuxリダイレクトの出力/入力文字を使用してデータベースをバックアップしていたときにこの問題が発生しました。そのため、構文を以下のように変更します。シモンズ:LinuxまたはMac端末を使用する。

バックアップ(>リダイレクトなし)

# mysqldump -u root -p databasename -r bkp.sql

復元(<リダイレクトなし)

# mysql -u root -p --default-character-set=utf8 databasename
mysql> SET names 'utf8'
mysql> SOURCE bkp.sql

「指定されたキーが長すぎました。最大キー長は767バイトです」という単純なエラーは消えました。

1
Cassio Seffrin

このクエリは、どの列が最大長に違反するインデックスを持っているかを検出するのに役立ちます。

SELECT
  c.TABLE_NAME As TableName,
  c.COLUMN_NAME AS ColumnName,
  c.DATA_TYPE AS DataType,
  c.CHARACTER_MAXIMUM_LENGTH AS ColumnLength,
  s.INDEX_NAME AS IndexName
FROM information_schema.COLUMNS AS c
INNER JOIN information_schema.statistics AS s
  ON s.table_name = c.TABLE_NAME
 AND s.COLUMN_NAME = c.COLUMN_NAME 
WHERE c.TABLE_SCHEMA = DATABASE()
  AND c.CHARACTER_MAXIMUM_LENGTH > 191 
  AND c.DATA_TYPE IN ('char', 'varchar', 'text')
1
Andrew

sql_modeが次のようになっているか確認してください

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

そうであれば、

sql_mode=NO_ENGINE_SUBSTITUTION

OR

my.cnfファイルを変更してサーバーを再起動します(以下を追加)。

innodb_large_prefix=on
1
neel

不平索引フィールドのCHARSETを "latin1"に変更してください。
ALTER TABLE tbl変更myfield myfield varchar(600)文字セットlatin1デフォルトNULL。
latin1は、4文字ではなく1文字に1バイトを使用します

0
Stan Holodnak

5つの回避策

この問題は、5.7.7(MariaDB 10.2.2?)で制限が引き上げられる前に存在していました。

CHARACTER SET utf8mb4を使用しようとしたために制限に達した場合。次に、エラーを回避するために次のいずれかを実行します(それぞれに欠点があります)。

  • 3072バイトの制限のために5.7.7(またはそれ以降)にアップグレードします。
  • VARCHARで255を191に変更します-191文字を超える値は失われます(ありそうにない?)。
  • ALTER .. CONVERT TO utf8-絵文字と一部の中国語を失います。
  • 「プレフィックス」インデックスを使用すると、パフォーマンス上の利点の一部が失われます。
  • または... 5.6/5.5/10.1のままで、4つのステップを実行して制限を3072バイトに上げます:

    SET GLOBAL innodb_file_format=Barracuda;
    SET GLOBAL innodb_file_per_table=1;
    SET GLOBAL innodb_large_prefix=1;
    logout & login (to get the global values);
    ALTER TABLE tbl ROW_FORMAT=DYNAMIC;  -- (or COMPRESSED)
    
0
Rick James

それを修正するために、これは私にとって魅力のように機能します。

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;
0

次のようなものを作成しているとします。

CREATE TABLE IF NOT EXISTS your_table (
  id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
  name varchar(256) COLLATE utf8mb4_bin NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY name (name)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

それはのようなものでなければなりません

CREATE TABLE IF NOT EXISTS your_table (
      id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(256) COLLATE utf8mb4_bin NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

ただし、コードからその列の一意性をチェックするか、新しい列をvarchar列のMD5またはSHA1として追加する必要があります。

0
10undertiber

インデックスの長さとMySQL/MariaDB


Laravelは、デフォルトでutf8mb4 characterセットを使用します。これにはデータベースに"emojis"を格納するためのサポートが含まれます。バージョン5.7.7より前のMySQLまたはバージョン10.2.2より前のMariaDBを実行している場合は、MySQLでインデックスを作成するために、移行によって生成されるデフォルトの文字列長を手動で設定する必要があります。あなたのAppServiceProvider:内でSchema :: defaultStringLengthメソッドを呼び出すことでこれを設定できます。

use Illuminate\Support\Facades\Schema;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Schema::defaultStringLength(191);
}

あるいは、{データベースに対してinnodb_large_prefixオプションを有効にすることもできます _このオプションを適切に有効にする方法については、データベースのドキュメントを参照してください。

公式のlaravelドキュメントからの参照:https://laravel.com/docs/5.7/migrations

0
Mayank Dudakiya