web-dev-qa-db-ja.com

MySQL:クエリで照合-副作用はありますか?

私のOpenCartテーブル照合順序はutf8_binですが、残念ながら、名前にアクセントが付いている製品名を検索することはできません。 Googleで検索したところ、アクセント互換で大文字と小文字を区別しない検索を行うには、照合順序をutf8_general_ciにする必要があることがわかりました。

検索クエリに照合宣言を追加するとどうなりますか?

SELECT * 
FROM  `address` 
COLLATE utf8_general_ci
LIMIT 0 , 30

(悪い)副作用はありますか?インデックス作成、パフォーマンスの問題について赤字ですか?それとも完全に安全ですか?

10
Adrian

クエリのパフォーマンスへの副作用、特にインデックスを使用する場合の副作用を考慮する必要があります。簡単なテストは次のとおりです。

mysql> create table aaa (a1 varchar(100) collate latin1_general_ci, tot int);
insert into aaa values('test1',3) , ('test2',4), ('test5',5);

mysql> create index aindex on aaa (a1);
Query OK, 0 rows affected (0.59 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc aaa;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| a1    | varchar(100) | YES  | MUL | NULL    |       |
| tot   | int(11)      | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.53 sec)


mysql> explain select * from aaa where a1='test1' ;
+----+-------------+-------+------+---------------+--------+---------+-------+--
----+-----------------------+
| id | select_type | table | type | possible_keys | key    | key_len | ref   | r
ows | Extra                 |
+----+-------------+-------+------+---------------+--------+---------+-------+--
----+-----------------------+
|  1 | SIMPLE      | aaa   | ref  | aindex        | aindex | 103     | const |
  1 | Using index condition |
+----+-------------+-------+------+---------------+--------+---------+-------+--
----+-----------------------+
1 row in set (0.13 sec)

mysql> explain select * from aaa where a1='test1' collate utf8_general_ci;
+----+-------------+-------+------+---------------+------+---------+------+-----
-+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows
 | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+-------------+
|  1 | SIMPLE      | aaa   | ALL  | NULL          | NULL | NULL    | NULL |    3
 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+-----
-+-------------+
1 row in set (0.06 sec)

別の照合を使用して検索すると、MySQLがa1のインデックスの使用を停止していることがわかります。これは、大きな問題になる可能性があります。

インデックスがクエリに使用されていることを確認するには、列の照合順序を最も頻繁に使用される照合順序に変更する必要がある場合があります。

3
Tim3880

SQLステートメントでのCOLLATEの使用 では、その使用法は見つかりません。とにかく、照合を使用した場合の影響に関する主な質問について説明するために、いくつかのヒントを見つけましたが、最初は次のとおりです。

dev.mysql.comから:

非バイナリ文字列(CHARVARCHAR、およびTEXTデータ型に格納されている)には、文字セットと照合順序があります。特定の文字セットには複数の照合順序を設定できます。各照合順序は、セット内の文字の特定の並べ替えおよび比較順序を定義します。

  1. 照合は、文字列の比較に使用される順序にすぎません。データの保存に使用される文字エンコードとは(ほとんど)関係ありません。照合順序は特定の文字セットでのみ使用できるため、照合順序を変更すると、文字エンコードが強制的に変更される可能性があるためです。
    文字エンコードが変更されている限り、MySQLは、シングルバイトからマルチバイトに、またはその逆に、新しい文字セットに値を正しく再エンコードします。列に対して大きくなりすぎる値は切り捨てられることに注意してください。[1]
  2. 文字列の比較が非常に単純で高速であるため、バイナリ照合の実際的な利点はその速度です。一般に、バイナリを使用したインデックスは、並べ替えに対して期待される結果を生成しない可能性がありますが、完全に一致する場合は便利です。[2]
  3. 複数のオペランドがあると、あいまいさが生じる可能性があります。例えば:

    _SELECT x FROM T WHERE x = 'Y';
    _

    比較では、列xの照合、または文字列リテラル_'Y'_の照合を使用する必要がありますか? xと_'Y'_の両方に照合順序があるので、どちらの照合順序が優先されますか?
    標準SQLは、以前は「強制力」ルールと呼ばれていたものを使用してこのような質問を解決します。 [3]

  4. フィールドの照合順序を変更すると、_ORDER BY_- [WHERE]-でもINDEXを使用できなくなります。したがって、驚くほど非効率的である可能性があります。 [4]
  5. 強制照合は列のエンコーディングと同じ文字セットで定義されるため、パフォーマンスへの影響はありません(その照合を列のデフォルトとして定義するのではなく、_utf8_general_ci_はほぼ確実に_utf8_bin_追加のルックアップ/計算が必要なため)。
    ただし、別の文字セットで定義された照合を強制した場合、MySQLは列の値をトランスコードする必要があります(パフォーマンスに影響します)。[5]
1
shA.t

これは役立つかもしれません: TF-8:General?Bin?Unicode?utf8_binも大文字と小文字を区別することに注意してください。そのため、テーブルの照合順序をutf8_general_ciに変更して、将来に備えて安心します。

0
MTP

可能であれば、列の定義を変更してください。

_ALTER TABLE tbl
    MODIFY col VARCHAR(...) COLLATE utf8_general_ci ...;
_

(列定義にすでに含まれている他のものをすべて含める必要があります。)変更する列が複数ある場合は、すべて同じALTERで実行します(速度を上げるため)。

何らかの理由でALTERを実行できない場合は、はい、SELECTを微調整して照合順序を変更できます。

あなたが言及したSELECTにはフィルタリング用のWHERE句がなかったので、テストケースを変更しましょう。

これがあるとしましょう。これは「サンノゼ」のみを検索します。

_SELECT *
    FROM tbl
    WHERE city = 'San Jose'
_

_San José_を含めるには:

_SELECT *
    FROM tbl
    WHERE city COLLATE utf8_general_ci = 'San Jose'
_

「アクセントの組み合わせ」がある場合は、utf8_unicode_ciの使用を検討してください。 発音区別符号の組み合わせの詳細 および トピックの詳細

副作用は?潜在的に大きいものを除いてなし:列のインデックスは使用できません。私の2番目のSELECT(上記)では、INDEX(city)は役に立たない。 ALTERは、SELECTに対するこのパフォーマンスの低下を回避しますが、1回限りのALTER自体はコストがかかります。

0
Rick James