776

何を入力するか 100% 確信が持てない一般的な Web サイトに対して、MySQL での照合に「最適な」選択があるかどうか疑問に思っています。MySQL、Apache、HTML、および PHP 内のすべてのものなど、すべてのエンコーディングが同じである必要があることを理解しています。

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

4

11 に答える 11

659

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

utf8_general_ciよりもいくらか高速ですがutf8_unicode_ci、精度は低くなります (並べ替えの場合)。特定の言語の utf8 エンコーディング( などutf8_swedish_ci) には、それらの言語の並べ替えを最も正確にする追加の言語規則が含まれています。utf8_unicode_ci特定の言語を好む正当な理由がない限り、ほとんどの場合(私は小さなパフォーマンスの向上よりも精度を優先します) を使用します。

特定のユニコード文字セットの詳細については、MySQL マニュアルを参照してください - http://dev.mysql.com/doc/refman/5.0/en/charset-unicode-sets.html

于 2008-12-15T07:58:27.243 に答える
141

utf8_unicode_ci実際には、おそらくorを使いたいでしょうutf8_general_ci

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

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

于 2008-12-15T08:02:37.700 に答える
120

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

utf8_general_ci照合が使用されている場合、MySQLは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;
于 2010-06-13T11:02:40.043 に答える
107

utf8mb4collat​​ion で文字セットを使用することをお勧めしますutf8mb4_unicode_ci

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

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

文字セットは、utf8mb42010 年 3 月 24 日に MySQL 5.5.3 で導入されました。

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

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

注:BarracudaからAntelopeに切り替えるには、MySQL サービスを複数回再起動する必要がある場合があります。innodb_file_format_maxは、MySQL サービスが再起動されるまで変更されません: 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

参考文献:

于 2014-08-24T19:57:22.880 に答える
47

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

charset unicodeのドキュメントの例:

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

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

于 2008-12-15T08:04:36.323 に答える
22

基本的に、文字列をどのように考えるかによって異なります。

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

私の著書では、「価値」が「価値」と等しくなることは決してありません。

テキスト フィールドを保存し、大文字と小文字を区別しない検索を実行する場合は、MYSQL 文字列関数を LOWER() や PHP 関数 strtolower() などの PHP 関数と共に使用します。

于 2010-12-07T01:42:37.667 に答える
13

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

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

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

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

于 2008-12-15T07:55:17.607 に答える
12

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

utf8_general_ci は MySQL の Unicode のデフォルトの照合であるため、utf8_unicode_ci を使用する場合は、多くの場所で指定する必要があります。

たとえば、すべてのクライアント接続には、デフォルトの文字セット (私には意味があります) だけでなく、デフォルトの照合 (つまり、Unicode の場合、照合は常にデフォルトで utf8_general_ci になります) もあります。

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

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

于 2013-07-30T13:20:02.873 に答える
8

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

ユーザーの照合ではなく、フィールドの検索を意​​図している場合は、utf8_general_ci または utf8_unicode_ci を使用します。どちらも大文字と小文字を区別せず、一方は大まかに一致します ('ß' は 's' と等しく、'ss' とは等しくありません)。utf8_german_ci のような言語固有のバージョンもあり、失われたマッチングは指定された言語により適しています。

[編集 - ほぼ 6 年後]

MySQL では「utf8」文字セットを推奨しなくなり、代わりに「utf8mb4」文字セットをお勧めします。それらはほぼ完全に一致しますが、もう少し (多くの) Unicode 文字を使用できます。

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

于 2012-05-08T13:27:45.163 に答える
5

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

たとえば、utf8_swedish_ci のチャートは次のとおりです。どの文字が同じと解釈されるかを示します。http://collat​​ion-charts.org/mysql60/mysql604.utf8_swedish_ci.html

于 2015-04-12T12:34:37.283 に答える
2

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

SET NAMES utf8;

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

于 2015-06-23T06:01:30.543 に答える