次のコマンドを実行したとき
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
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の制限を超えずに目的のビジネスルールを実装できるようにする改善があるかどうかを検討する価値があるでしょうか。
INNODB/Utf-8でVARCHAR(256)
フィールドにUNIQUE
インデックスを付けようとしている人に問題がある場合は、VARCHAR(255)
に切り替えてください。 255が制限のようです。
あなたが限界に達すると。以下を設定してください。
utf8
VARCHAR(255)
utf8mb4
VARCHAR(191)
MySQLは、文字列の1文字あたりのバイト数が最悪の場合を想定しています。 MySQLの 'utf8'エンコーディングでは、そのエンコーディングはU+FFFF
を超える文字を許可しないため、1文字あたり3バイトです。 MySQLの「utf8mb4」エンコーディングの場合、これは1文字あたり4バイトです。これは、MySQLが実際のUTF-8と呼ぶものだからです。
ですから、あなたが 'utf8'を使っていると仮定すると、あなたの最初のカラムはインデックスの60バイトを取り、そしてあなたのもう二つは1500を取ります。
クエリの前にこのクエリを実行します。
SET @@global.innodb_large_prefix = 1;
これは3072 bytes
の上限を増やすでしょう。
どの文字エンコーディングを使用していますかUTF-16などの一部の文字セットは、1文字に複数のバイトを使用します。
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
オプションを有効にすることもできます。このオプションを正しく有効にする方法については、データベースのマニュアルを参照してください。
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文字です。
[A-z0-9\-]
文字のみを使用し、1文字あたり1バイトのみを使用するlatin1_general_ci
を使用し、列の長さは767バイトにできるようにしました。UNIQUE
キーがあります。また、検索速度を上げるために、元のSEO列にKEY
インデックスを追加します。なぜエラーメッセージが表示されるのかについての回答は、ここですでに多くのユーザーによって回答されています。私の答えは、それをどのように修正して使用するかということです。
このリンクから を参照。
use my_database_name;
データベースが変更されました
set global innodb_large_prefix=on;
クエリOK、影響を受けた0行(0.00秒)
set global innodb_file_format=Barracuda;
クエリOK、影響を受けた0行(0.02秒)
この修正の問題は、dbを別のサーバにエクスポートする場合(たとえばlocalhostから実際のHostへ)、そのサーバでMySQLコマンドラインを使用できない場合です。あなたはそれをそこで動作させることはできません。
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バイトを破らないでください。
あなたは長い列のmd5の列を追加することができます
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
のように:
通常のインデックスはMySQLがデータをより迅速に検索するのを助けるために使用されるだけなので、これは問題ありません。フィールド全体を索引付けする必要はありません。
では、なぜMySQLは通常のインデックスに対して自動的にインデックスを切り捨てますが、ユニークインデックスに対してそれを行おうとすると明示的なエラーを投げますか? MySQLが挿入または更新される値がすでに存在するかどうかを判断できるようにするには、その一部ではなく実際に値全体にインデックスを付ける必要があります。
一日の終わりに、フィールドに一意のインデックスを付けたい場合は、フィールドの内容全体がインデックスに収まる必要があります。 utf8mb4の場合、これはVARCHARフィールドの長さを191文字以下に減らすことを意味します。そのテーブルまたはフィールドにutf8mb4が必要ない場合は、utf8にドロップして、255個の長さのフィールドを保持することができます。
これが私の最初の答えです。
データベースを削除してこのように再作成するだけで、エラーはなくなります。
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
このトピックを検索したところ、ついにカスタム変更がありました
MySQLワークベンチ6.3.7バージョンの場合グラフィカルインターフェーズが利用可能です
6.3.7より下のバージョンでは直接オプションは利用できないのでコマンドプロンプトで行く必要がある
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
私はこの問題を修正しました:
varchar(200)
交換された
varchar(191)
200を超えるすべてのvarcharは、それらを191に置き換えるかテキストに設定します。
照合順序を変更してください。あなたは utf8_general_ci を使うことができます
テーブル作成時にutf8mb4
をutf8
に変更するだけで私の問題は解決しました。例えば、CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
からCREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
までです。
Laravel 5.7または5.6の場合
従うべきステップ
App\Providers\AppServiceProvider.php
に行きます。use Illuminate\Support\Facades\Schema;
にこれを追加してください。Schema::defaultStringLength(191);
そのすべて、お楽しみください。
以下の列に基づいて、これら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
接頭辞の制限により、このエラーが発生します。 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;
私の場合、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バイトです」という単純なエラーは消えました。
このクエリは、どの列が最大長に違反するインデックスを持っているかを検出するのに役立ちます。
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')
sql_mode
が次のようになっているか確認してください
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
そうであれば、
sql_mode=NO_ENGINE_SUBSTITUTION
OR
my.cnfファイルを変更してサーバーを再起動します(以下を追加)。
innodb_large_prefix=on
不平索引フィールドのCHARSETを "latin1"に変更してください。
ALTER TABLE tbl変更myfield myfield varchar(600)文字セットlatin1デフォルトNULL。
latin1は、4文字ではなく1文字に1バイトを使用します
5つの回避策
この問題は、5.7.7(MariaDB 10.2.2?)で制限が引き上げられる前に存在していました。
CHARACTER SET utf8mb4
を使用しようとしたために制限に達した場合。次に、エラーを回避するために次のいずれかを実行します(それぞれに欠点があります)。
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)
それを修正するために、これは私にとって魅力のように機能します。
ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;
次のようなものを作成しているとします。
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として追加する必要があります。
インデックスの長さと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