MySQLのイケてない仕様 - VARCHAR編

カテゴリ: WEB、WEBシステム関連 / 公開日: 2010年6月09日(水曜)01:50 / 投稿者: Tom Goodsun

今日、MySQLは「LAMP」という言葉が示すように非常にその存在感は強い。
高速性が売りだったり、オープンソースということもあいまって非常に人気のあるベンダーのひとつだ。

今も今後も、ウェブシステムはMySQLが主流になるだろう。
個人的にはSun Micro SystemsがOracleに買収され、さらにDBベンダーのOracleがMySQLを買収して、そのオープン性が失われやしないかが心配だ。
また、MySQLがさらにOracleに近い仕様へバージョンアップするのではという期待もあったりする。

今回の問題はこの人気のMySQLのイケてない仕様について。
普通に使っていては気づかない、とてもマニアックな仕様ともいえるが、これが結構ハマるんではないだろうか。
皆さんは知っているかな?

文字列型は英大文字・小文字を区別しない。

参考URL http://homepage.mac.com/kurusaki/iblog/C1305251996/E138576755/index.html

どういうことかわかるだろうか?
実はVARCHAR型などの文字列型はデフォルトでは英字の大文字・小文字を判別するような仕様になっていない。だがデータ的には大文字・小文字はそのまま出力される。
たとえば条件句に「WHERE data = 'abcdef'」としたとする。この条件ではdataというフィールドに格納されている「abcdef」はもちろん、「ABCDEF」や「Abcdef」、「abCdEf」といったデータも条件対象になってしまう。
感覚的には「WHERE data = 'abcdef'」は「abcdef」にしかヒットしないと考えるがデフォルトはそうでない。

これを回避する方法はいくつかあるが、CREATE TABLEの段階で回避できていたほうがいいだろう。

それにはフィールドにBINARY属性を付与する。たとえば以下のような書き方になる。

CREATE TABLE sample_table (
  id SERIAL,
  char1 VARCHAR(120),
  char2 VARCHAR(120) BINARY,
  modified_date TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  INDEX char1_index (char1),
  INDEX char2_index (char2)
) ENGINE=InnoDB CHARACTER SET 'utf8';

この例ではフィールドchar1は大文字・小文字を区別しない、フィールドchar2は区別することになる。ちなみにBINARY属性を使用しなかった場合でも、条件に指定して精度を高めることができる。実際にデータをいくつか投入してみて確認してほしい。

INSERT INTO sample_table (char1, char2) VALUES
('test', 'test'),('TEST', 'TEST'),('tEST', 'tEST'),('Test', 'Test');

-- 1.取得件数は4件
SELECT * FROM sample_table WHERE char1 = 'TEST';

-- 2.取得件数は1件(条件で処理する場合)
SELECT * FROM sample_table WHERE BINARY char1 = 'TEST';

-- 3.取得件数は1件
SELECT * FROM sample_table WHERE char2 = 'TEST';

ちなみにどちらのフィールドにもインデックスを張っているが、2のSELECT文のように条件でBINARY指定しちゃうとインデックスは効かないみたい。
explainの結果の見方は以下のサイトを参考に。「type」が「ALL」はフルテーブルスキャン。インデックス使わないので、VARCHAR型のフィールドにインデックス張る場合やもちろんデータ件数が多くなることが予想され、このフィールドを条件対象にする場合は、CREATE TABLEで指定しておくのが必須でだろう。

参考サイト 漢(オトコ)のコンピュータ道 MySQLの EXPLAINを徹底解説!!

 

id select_type table type possible_keys key key_len ref rows Extra
explain SELECT * FROM sample_table WHERE char1 = 'TEST';
1 SIMPLE sample_teble ref char1_index char1_index 363 const 2 Using where
explain SELECT * FROM sample_table WHERE BINARY char1 = 'TEST';
1 SIMPLE sample_teble ALL



4 Using where
explain SELECT * FROM sample_table WHERE char2 = 'TEST';
1 SIMPLE sample_teble ref char2_index char2_index 363 const 1 Using where

当然ながらOracleやPostgreSQLといったものはデフォルトで大文字・小文字は区別してくれる。

ちなみにphpMyAdminで見てみると、ちゃんと書いてくれていた・・・。