Varchar 在 MySql 中是一個很普通的欄位,幾乎每個 Table 都會被使用到,但是你知道 varchar(10)
這個設定到底可以存幾個字嗎,是 10 個英文字,還是 10 個中文字呢,一個 UTF8 編碼的中文字是需要 三個 bytes 的空間儲存,如果我們需要存 10 個中文字,那麼 Mysql 欄位至少要有 30 Bytes 的空間,那問題就來了, varchar(10)
到底是 10 bytes 還是 30 bytes ?
如果 varchar(10)
代表 30 bytes 空間,那麼我們可以存 10 個中文字或是 30 個英文字,若 varchar(10)
代表 10 bytes 的空間,那麼我們只能存 3 個中文字或 10 個英文字,這樣聽起來是非常的不合理,有些 Table Field 例如 username ,這個欄位通常會被設計成固定字串長度,也就是說不管中英文字,我們希望他的最大字數是固定的。
其實 MySql varchar 中的 char 代表一個字元,不管是中文字或英文字都算一個,所以你定義了 varchar(10)
代表你只能存放 10 個英文字或 10 個中文字,這樣還不算解決問題,因為剛剛有說 10 個中文字需要 30 Bytes 的空間,假如我只需要存 10 個英文字,那麼我總共只需要 10 Bytes 的空間,那另外多出來的 20 Bytes 不就浪費了嗎?
utf8_unicode_ci vs ascii_bin
好在 MySql 有定義很多種 collations 來處理 varchar,如果我們的 varchar 欄位只需要存放英文跟特殊符號,可以使用 ascii_bin
這個 collations, 如果 varchar 欄位需要存放中文字,那麼我們就要使用 utf8_unicode_ci
或是 utf8_general_ci
。
例如我先建立一個 Table,分別定義了 varchar(10) ascii_bin
與 varchar(10) utf8_unicode_ci
:
- CREATE TABLE `test` (
- `ename` varchar(10) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL,
- `cname` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL
- ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
varchar(10) ascii_bin
接著我在 ename 這個欄位插入兩筆資料,分別是 10 個英文字 與 1 個中文字,英文字的部分可以正確的存入,但是中文字就會被阻擋下來,請看下面的 demo。
- mysql> insert into test(ename) values('0123456789');
- Query OK, 1 row affected (0.00 sec)
- mysql> insert into test(ename) values('中');
- ERROR 1366 (HY000): Incorrect string value: '\xE4\xB8\xAD' for column 'ename' at row 1
varchar(10) utf8_unicode_ci
再來我要在 cname 這個欄位插入兩筆資料,分別是 10 個中文字與 11 個中文字,第二筆資料會因為長度過長而被擋下。
- mysql> insert into test(cname) values('一二三四五六七八九十');
- Query OK, 1 row affected (0.00 sec)
- mysql> insert into test(cname) values('一二三四五六七八九十一');
- ERROR 1406 (22001): Data too long for column 'cname' at row 1
對 MySql 來說 varchar(10) collate ascii_bin
這種欄位只佔了 10 Bytes ,而且只能儲存英文字與符號,varchar(10) collate utf8_unicode_ci
與 varchar(10) collate utf8_general_ci
則是佔了 30 Bytes ,可以儲存英文字,符號,跟中文字,如果你的欄位只需要儲存英文,那麼請使用 ascii_bin
就好。
field | collate | value | valid |
---|---|---|---|
varchar(10) | ascii_bin | 10個英文 | O |
varchar(10) | ascii_bin | 11個英文 | X |
varchar(10) | ascii_bin | 1個中文 | X |
varchar(10) | utf8_unicode_ci | 10個英文 | O |
varchar(10) | utf8_unicode_ci | 11個英文 | X |
varchar(10) | utf8_unicode_ci | 10個中文 | O |
varchar(10) | utf8_unicode_ci | 11個中文 | X |
utf8_general_ci vs utf8_unicode_ci
精準度
utf8_unicode_ci 在對文字排序上會比 utf8_general_ci 更精準,utf8_general_ci 是一種簡易化的排序規則,這個規則會把一些相似文字的定義成同一層排序,所以文字排序上無法像 utf8_unicode_ci 這麼精準。
例如有一些拉丁字母 "ÀÁÅåāă" ,在 utf8_general_ci 的排序規則下,是等同於英文字母 "A"。
效能
utf8_general_ci 的效能會比 utf8_unicode_ci 好一些,不過影響的效能很小,很難感受出差別,除非你真的很在意這種微小的效能差異,否則建議使用 utf8_unicode_ci 就可以了。