MySQLとPHPを使用するのに最適な照合は何ですか?[閉まっている]


731

入力する内容が100%わからない一般的なWebサイトのMySQLの照合に「最適な」選択肢があるかどうか疑問に思います。MySQL、Apache、HTML、PHP内のすべてなど、すべてのエンコーディングが同じであることを理解しています。

過去に「UTF-8」で出力するようにPHPを設定しましたが、これはMySQLでどの照合に一致しますか?私はそれがUTF-8のものの一つだと思っていますが、私は使用しているutf8_unicode_ciutf8_general_ciutf8_bin前に。


35
補足:MySQLの「utf8」は適切なUTF-8ではありませんが(𝌆のような4バイト以上のUnicode文字はサポートされていません)、「utf8mb4」はそうです。utf8では、サポートされていない最初のUnicode文字で始まる挿入時にフィールドが切り捨てられます。mathiasbynens.be/notes/mysql-utf8mb4
basic6

6
私たちが今までのすべてのこれらの絵文字について5バイトが必要になります場合、私は...不思議ため息
アルバロ・ゴンサレス

1
関連質問:stackoverflow.com/questions/38228335/…「どのMySQL照合がPHPの文字列比較と正確に一致するか」
William Entriken 2016

回答:


618

主な違いは、並べ替えの精度(言語の文字を比較する場合)とパフォーマンスです。唯一の特別なものは、バイナリ形式の文字を比較するためのutf8_binです。

utf8_general_ciはやや高速ですがutf8_unicode_ci、(ソートの場合)精度は低くなります。特定の言語のUTF8エンコーディング(などはutf8_swedish_ci)それら最も正確なこれらの言語のためにソートする作る追加の言語ルールが含まれています。utf8_unicode_ci特定の言語を使用する正当な理由がない限り、ほとんどの場合、私は使用します(小さなパフォーマンスの改善よりも精度を優先します)。

MySQLマニュアルで特定のUnicode文字セットの詳細を読むことができます-http ://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html


4
小さなパフォーマンスの改善?あなたはこれについて確信を持っていますか ?publib.boulder.ibm.com/infocenter/db2luw/v9r5/index.jsp?topic=/…選択した照合順序は、データベース内のクエリのパフォーマンスに大きな影響を与える可能性があります。
Adam Ramadhan、2010

62
これはMySQLではなくDB2用です。また、具体的な数値やベンチマークはないため、作者の意見に基づいているだけです。
エランガルペリン2010

3
関数を使用したい場合、MySQL(ほとんどの現在配布されているバージョン)にバグがあり、関数が常にutf8_general_ciを使用して文字列を返すため、文字列に別の照合順序を使用している場合に問題が発生することに注意して
El Yobo

1
私がいつも使用するさまざまなロケールでの経験からutf8_unicode_*
Shiplu Mokaddim

11
更新:新しいバージョンについてはutf8mb4、およびをお勧めしutf8mb4_unicode_520_ciます。これにより、中国語の残りの部分に加えて、照合が改善されます。
リックジェームズ

129

実際には、utf8_unicode_ciまたはを使用する必要がありutf8_general_ciます。

  • utf8_general_ci すべてのアクセントを取り除いて並べ替え、ASCIIのように並べ替えます。
  • utf8_unicode_ci Unicodeのソート順を使用するため、より多くの言語で正しくソートされます

ただし、これを使用して英語のテキストを保存するだけの場合は、これらに違いはありません。


1
私はあなたの説明が好きです!いいね。しかし、Unicodeの並べ替え順序がアクセントを取り除くよりも正確に並べ替える方法として優れている理由を正確に理解する必要があります。
weiaデザイン2013

14
@アダムそれは本当にあなたのターゲットオーディエンスに依存します。並べ替えは、正しくローカライズするのが難しい問題です。たとえば、ノルウェー語では、ÆØÅはアルファベットの最後の3文字です。utf8_general_ciを使用すると、ØとÅはOとAに変換され、並べ替えると完全に間違った位置に配置されます(Æは合字であり、アクセント付き文字ではないため、処理方法がわかりません)。この並べ替え順序は、ほとんどすべての言語で異なります。たとえば、ノルウェー語とスウェーデン語では順序が異なります(文字はわずかに異なるため、同じと見なされます)。Unicodeはこれを修正します。
Vegard Larsen 2013

だから私が基本的に言っていることは、可能であればおそらく言語固有のソートを使用すべきだということですが、ほとんどの場合それは実行不可能なので、Unicodeの一般的なソートを行ってください。一部の言語ではまだ奇妙ですが、ASCIIよりも正確です。
Vegard Larsen

3
@Manatax-いずれかのutf8_照合順序では、データはutf8として保存されます。照合は、どの文字が等しいと見なされるか、およびそれらがどのように順序付けられるかについてです。
frymaster 2013年

2
@frymaster -ではない本当のは、あたりとして:mathiasbynens.be/notes/mysql-utf8mb4「MySQLのUTF8は、あなただけが可能なすべてのUnicodeコードポイントの5.88パーセント保存することができます」
データ

120

を使用するときに発生する可能性があるこの問題に十分に注意してくださいutf8_general_ci

MySQLは、utf8_general_ci照合が使用されている場合、selectステートメントの一部の文字を区別しません。これは非常に厄介なバグにつながる可能性があります-特に、たとえば、ユーザー名が関係している場合。データベーステーブルを使用する実装によっては、この問題により、悪意のあるユーザーが管理者アカウントと一致するユーザー名を作成する可能性があります。

この問題は、少なくとも初期の5.xバージョンで発生します-この動作が後で変更されたかどうかはわかりません。

私はDBAではありませんが、この問題を回避するためutf8-binに、大文字と小文字を区別せずに常に使用します。

以下のスクリプトは、例を挙げて問題を説明しています。

-- first, create a sandbox to play in
CREATE DATABASE `sandbox`;
use `sandbox`;

-- next, make sure that your client connection is of the same 
-- character/collate type as the one we're going to test next:
charset utf8 collate utf8_general_ci

-- now, create the table and fill it with values
CREATE TABLE `test` (`key` VARCHAR(16), `value` VARCHAR(16) )
    CHARACTER SET utf8 COLLATE utf8_general_ci;

INSERT INTO `test` VALUES ('Key ONE', 'value'), ('Key TWO', 'valúe');

-- (verify)
SELECT * FROM `test`;

-- now, expose the problem/bug:
SELECT * FROM test WHERE `value` = 'value';

--
-- Note that we get BOTH keys here! MySQLs UTF8 collates that are 
-- case insensitive (ending with _ci) do not distinguish between 
-- both values!
--
-- collate 'utf8_bin' doesn't have this problem, as I'll show next:
--

-- first, reset the client connection charset/collate type
charset utf8 collate utf8_bin

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Note that we get just one key now, as you'd expect.
--
-- This problem appears to be specific to utf8. Next, I'll try to 
-- do the same with the 'latin1' charset:
--

-- first, reset the client connection charset/collate type
charset latin1 collate latin1_general_ci

-- next, convert the values that we've previously inserted
-- in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET latin1 COLLATE latin1_general_ci;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Again, only one key is returned (expected). This shows 
-- that the problem with utf8/utf8_generic_ci isn't present 
-- in latin1/latin1_general_ci
--
-- To complete the example, I'll check with the binary collate
-- of latin1 as well:

-- first, reset the client connection charset/collate type
charset latin1 collate latin1_bin

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET latin1 COLLATE latin1_bin;

-- now, re-check for the bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Again, only one key is returned (expected).
--
-- Finally, I'll re-introduce the problem in the exact same 
-- way (for any sceptics out there):

-- first, reset the client connection charset/collate type
charset utf8 collate utf8_generic_ci

-- next, convert the values that we've previously inserted in the table
ALTER TABLE `test` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

-- now, re-check for the problem/bug
SELECT * FROM test WHERE `value` = 'value';

--
-- Two keys.
--

DROP DATABASE sandbox;

36
-1:これは、関連する列に一意のキーを適用することによって確実に修正されます。2つの値が'value'およびの場合、同じ動作が見られます'valUe'。照合の要点は、2つの文字列が互いに等しいと見なされる場合に(特に)ルールを提供することです。
Hammerite

13
それはまさに私が説明しようとしている問題です-照合は2つのものを等しくしますが、実際にはまったく等しいことを意図していません(したがって、一意の制約は、達成したいものとは正反対です)
Guus

18
しかし、それが「問題」であり、照合が意図したとおりの動作である場合に「バグ」につながると説明します。説明は正しいですが、不適切な照合を選択するのはDBA側のエラーであるだけです。
Hammerite 2011

32
重要なのは、照合で等しいと見なされる2つのユーザー名を入力するときに、列のユーザー名を一意に設定した場合は許可されないことです。もちろん、そうする必要があります。
ホグワーツの生徒

12
この回答と@Hammeriteのコメントの両方に賛成しました。これらの両方を組み合わせると、照合順序を理解するのに役立ちました。
Nacht-モニカを2015年

86

utf8mb4照合では文字セットを使用するのが最適utf8mb4_unicode_ciです。

文字セットはutf8、可能な文字数の約6%の少量のUTF-8コードポイントのみをサポートしています。utf8Basic Multilingual Plane(BMP)のみをサポートします。他に16機あります。各プレーンには65,536文字が含まれています。utf8mb417面すべてをサポートします。

MySQLは4バイトのUTF-8文字を切り捨て、データが破損します。

このutf8mb4文字セットは2010-03-24のMySQL 5.5.3で導入されました。

新しい文字セットを使用するために必要な変更の一部は簡単ではありません。

  • アプリケーションデータベースアダプタで変更を行う必要がある場合があります。
  • 文字セット、照合順序の設定、innodb_file_formatのバラクーダへの切り替えなど、my.cnfに変更を加える必要があります。
  • SQL CREATEステートメントには、以下を含める必要がある場合があります。 ROW_FORMAT=DYNAMIC
    • VARCHAR(192)以上のインデックスにはDYNAMICが必要です。

注:BarracudaからAntelopeに切り替えると、MySQLサービスを複数回再起動する必要がある場合があります。innodb_file_format_maxMySQLサービスが次のように再起動されるまで変更されませんinnodb_file_format = barracuda

MySQLは古いAntelopeInnoDBファイル形式を使用します。Barracudaは動的行フォーマットをサポートします。これは、文字セットに切り替えた後にインデックスとキーを作成するためのSQLエラーにヒットしたくない場合に必要です。utf8mb4

  • #1709-インデックス列のサイズが大きすぎます。列の最大サイズは767バイトです。
  • #1071-指定されたキーが長すぎました。キーの最大長は767バイトです

次のシナリオはMySQL 5.6.17でテストされています。デフォルトでは、MySQLは次のように構成されています。

SHOW VARIABLES;

innodb_large_prefix = OFF
innodb_file_format = Antelope

MySQLサービスを停止し、既存のmy.cnfにオプションを追加します。

[client]
default-character-set= utf8mb4

[mysqld]
explicit_defaults_for_timestamp = true
innodb_large_prefix = true
innodb_file_format = barracuda
innodb_file_format_max = barracuda
innodb_file_per_table = true

# Character collation
character_set_server=utf8mb4
collation_server=utf8mb4_unicode_ci

SQL CREATEステートメントの例:

CREATE TABLE Contacts (
 id INT AUTO_INCREMENT NOT NULL,
 ownerId INT DEFAULT NULL,
 created timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 contact VARCHAR(640) NOT NULL,
 prefix VARCHAR(128) NOT NULL,
 first VARCHAR(128) NOT NULL,
 middle VARCHAR(128) NOT NULL,
 last VARCHAR(128) NOT NULL,
 suffix VARCHAR(128) NOT NULL,
 notes MEDIUMTEXT NOT NULL,
 INDEX IDX_CA367725E05EFD25 (ownerId),
 INDEX created (created),
 INDEX modified_idx (modified),
 INDEX contact_idx (contact),
 PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB ROW_FORMAT=DYNAMIC;
  • がCREATEステートメントから削除されたINDEX contact_idx (contact)場合に生成されたエラー#1709を確認できますROW_FORMAT=DYNAMIC

注:インデックスを変更して最初の128文字に制限すると、contactBarracudaをROW_FORMAT=DYNAMIC

INDEX contact_idx (contact(128)),

また、フィールドのサイズがVARCHAR(128)であると表示されている場合、128バイトではありません。128バイトの4バイト文字または128バイトの1バイト文字を使用できます。

このINSERTステートメントには、2行に4バイトの「poo」文字が含まれている必要があります。

INSERT INTO `Contacts` (`id`, `ownerId`, `created`, `modified`, `contact`, `prefix`, `first`, `middle`, `last`, `suffix`, `notes`) VALUES
(1, NULL, '0000-00-00 00:00:00', '2014-08-25 03:00:36', '1234567890', '12345678901234567890', '1234567890123456789012345678901234567890', '1234567890123456789012345678901234567890', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678', '', ''),
(2, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '', ''),
(3, NULL, '0000-00-00 00:00:00', '2014-08-25 03:05:57', 'poo', '12345678901234567890', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '123💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩💩', '', '');

last列で使用されているスペースの量を確認できます。

mysql> SELECT BIT_LENGTH(`last`), CHAR_LENGTH(`last`) FROM `Contacts`;
+--------------------+---------------------+
| BIT_LENGTH(`last`) | CHAR_LENGTH(`last`) |
+--------------------+---------------------+
|               1024 |                 128 | -- All characters are ASCII
|               4096 |                 128 | -- All characters are 4 bytes
|               4024 |                 128 | -- 3 characters are ASCII, 125 are 4 bytes
+--------------------+---------------------+

データベースアダプタで、接続の文字セットと照合順序を設定することができます。

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'

PHPでは、これは次のように設定されます。 \PDO::MYSQL_ATTR_INIT_COMMAND

参照:




6
utf8mb4_unicode_ciは、2015年の新しいプロジェクトに推奨される照合順序です。–
Trevor Gehman

7
更新... utf8mb4_unicode_520_ciが良いです。utf8mb4_unicode_800_ciMySQLがUnicode標準に追いつくにつれて、将来的には(またはそのようなもの)が存在するでしょう。
リックジェームズ

46

照合順序は、データの並べ替え方法と文字列の相互比較方法に影響します。つまり、ほとんどのユーザーが期待する照合順序を使用する必要があります。

charset unicodeドキュメントの例:

utf8_general_ci'ß'が 'ss'ではなく 'ss'と等しいことを除いて、ドイツ語とフランス語の両方で満足です。これがアプリケーションで許容できる場合utf8_general_ciは、高速であるため使用する必要 があります。それ以外の場合は、utf8_unicode_ciより正確であるため、使用してください。

したがって、それは予想されるユーザーベースと、正しいソートがどれだけ必要かによって異なります。英語のユーザーベースの場合utf8_general_ciは十分ですが、スウェーデン語などの他の言語の場合、特別な照合順序が作成されています。


1
私はutf8_general_ciを使用していたし、ソートやarmscii_general_ciながら、それは非常にこれはあなたがソーシャルネットワーキングサイトで使用されている照合どう思いますかもう一つの質問を、起こっquick.Whyなかったことは、第二のカップルを取った?

22

本質的には、文字列の考え方に依存します。

Guusによって強調された問題のため、私は常にutf8_binを使用しています。私の意見では、データベースに関する限り、文字列はまだ単なる文字列です。文字列は、UTF-8文字の数です。文字にはバイナリ表現があるのに、なぜ使用している言語を知る必要があるのですか?通常、人々は多言語サイトを対象としたシステムのデータベースを構築します。これが、UTF-8を文字セットとして使用することの要点です。私は少し純粋主義者ですが、バグのリスクは、インデックス作成で得られるわずかな利点を大幅に上回っていると思います。言語関連のルールは、DBMSよりもはるかに高いレベルで実行する必要があります。

私の本では、「価値」が100万年の間「価値」に等しくなることはありません。

テキストフィールドを格納し、大文字と小文字を区別しない検索を行う場合、LOWER()やphp関数strtolower()などのPHP関数でMYSQL文字列関数を使用します。


9
文字列のバイナリ比較が目的の比較である場合は、もちろんバイナリ照合を使用する必要があります。ただし、代替照合を「バグリスク」として却下する、または単にインデックス付けの便宜を図ることは、照合のポイントを完全に理解していないことを示唆しています。
Hammerite

13

UTF-8テキスト情報については、次のutf8_general_ci理由から使用する必要があります...

  • utf8_bin:文字列内の各文字のバイナリ値で文字列を比較します

  • utf8_general_ci:一般的な言語規則を使用し、大文字と小文字を区別しない比較を使用して文字列を比較します

別名、データの検索とインデックス作成をより高速に、より効率的に、より便利にする必要があります。


12

受け入れられた回答はかなり明確にutf8_unicode_ciを使用することを示唆しています。そして、素晴らしい新しいプロジェクトでは、誰かが時間を節約できるように、最近の反対の経験を関連付けたいと思いました。

utf8_general_ciはMySQLのUnicodeのデフォルトの照合順序であるため、utf8_unicode_ciを使用したい場合は、 多くのの場所。

たとえば、すべてのクライアント接続には、デフォルトの文字セット(私には理にかなっています)だけでなく、デフォルトの照合も含まれています(つまり、照合は常にデフォルトでunifのutf8_general_ciになります)。

同様に、フィールドにutf8_unicode_ciを使用する場合、データベースに接続するスクリプトを更新して、目的の照合を明示的に記述する必要があります。そうしないと、接続がデフォルトの照合を使用している場合、テキスト文字列を使用したクエリが失敗する可能性があります。

その結果、任意のサイズの既存のシステムをUnicode / utf8に変換すると、MySQLがデフォルトを処理する方法が原因で、utf8_general_ciを使用せざるを得なくなる可能性があります。


8

Guusによって強調されたケースについては、utf8_bin(厳密なマッチング、誤った順序)ではなく、utf8_unicode_cs(大文字と小文字を区別、厳密なマッチング、ほとんどの場合正しく順序付け)のいずれかを使用することを強くお勧めします。

ユーザーに対して一致させるのではなく、フィールドを検索する場合は、utf8_general_ciまたはutf8_unicode_ciを使用します。どちらも大文字と小文字を区別しません。1つはほとんど一致しません( 'ß'は 's'と等しく、 'ss'とは等しくありません)。utf8_german_ciのような言語固有のバージョンもあり、ここで指定した言語に一致の喪失がより適しています。

[編集-ほぼ6年後]

MySQLで「utf8」文字セットを推奨しなくなりましたが、代わりに「utf8mb4」文字セットを推奨します。ほぼ完全に一致しますが、Unicode文字を少し(多く)増やすことができます。

実際には、MySQLは「utf8」文字セットとそれぞれの照合を「utf8」仕様に一致するように更新する必要がありますが、代わりに、不完全な「utf8」文字セットをすでに使用しているストレージの指定に影響しないように、個別の文字セットとそれぞれの照合を更新します。 。


5
参考:utf8_unicode_cs存在しません。大文字と小文字が区別される唯一のutf8はutf8_binです。問題は、utf8_binソートが正しくないことです。参照:stackoverflow.com/questions/15218077/...
コスタ

1
更新してくれてありがとう!
プロメテウス

5

これらの照合チャートは役に立ちました。http://collat​​ion-charts.org/mysql60/。どちらが使用されているutf8_general_ciかはわかりません。

たとえば、次はutf8_swedish_ciのグラフです。どの文字が同じものとして解釈されるかを示します。http://collat​​ion-charts.org/mysql60/mysql604.utf8_swedish_ci.html


チャートの異なるフレーバー: mysql.rjweb.org/utf8_collat​​ions.html
Rick James

2

データベースアップロードファイルで、次の行をすべての行の前に追加します。

SET NAMES utf8;

そして、あなたの問題は解決されるべきです。


2
質問を読む:過去に「UTF-8」で出力するようにPHPを設定しましたが、これはMySQLでどの照合に一致しますか?UTF-8の1つだと思いますが、以前はutf8_unicode_ci、utf8_general_ci、utf8_binを使用していました。
Jitesh Sojitra

5
この答えは質問とは関係ありません。さらに、SET NAMESクエリを直接発行しても、クライアントにエンコーディングが認識されず、準備されたステートメントなどの特定の機能が非常に微妙に壊れる可能性があります。
アルバロ・ゴンサレス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.