MYSQL校對規則


一、前言

有時候遇到這種情況,你用一個like語句查詢,查到的結果中有一些並沒有包含你查詢的關鍵詞的紀錄;
有時候遇到這種情況,你的數據庫自作聰明的大小寫不敏感,讓你在更新時把大小寫不同的兩條記錄都更新了;
有時候遇到這種情況,你的查詢語句一切正常,查詢卻失敗了,報告Illegal mix of collations錯誤;

你很困惑,在想數據庫是不是壞了。。。其實 ,這些都和數據庫字符集的校對規則有關;了解了校對規則,你就知道怎樣處理這些問題。

那么,校對規則是怎么回事呢?它是一組規則,負責決定某一字符集下的字符進行比較和排序的結果。

比如說,有latin1字符集中的字母A和a,我們需要它們在比較的時候相等,那么,我們可以使用字符集校對規則 latin1_general_ci;這種校對規則在比較和排序的時候不區分大小寫;如果我們需要他們在比較的時候不等呢?也很簡單,我們可以使用字符集校對規則latin1_bin;這種校對規則會以二進制的方式對字符進行比較,很明顯,a和A的二進制編碼不同,比較的結果就是不等。

上面的場景說明了校對規則在最簡單情況下起的作用;實際情況與此並沒有太多不同,只不過稍微有些復雜而已。

 

二、校對規則總覽

我們可以使用 SHOW COLLATION 指令來查看數據庫支持的校對規則

在圖中,我們列出了數據庫支持的latin1字符集的校對規則。為什么一種字符集竟然有這么多種的校對規則呢?因為在不同的情況下,對比較的結果有不同的期待,所以就有了不同的校對規則。前面說的大小寫敏感(latin1_general_cs)和不敏感(latin1_general_ci)是兩種校對規則,根據二進制方式進行比較(latin1_bin)也是一種校對規則,德國人(latin1_german1_ci)和西班牙人(latin1_spanish_ci)使用的某些不同的拉丁字符在某些情況下是等價的,所以有了兩種新的校對規則。

舉個例子,在latin1_german1_ci中,如下字符是等價的,而他們,具有不同的外形和編碼。當然,它們的編碼不同,所以在latin1_bin校對規則下,他們又是不等價的了。
A,a,À,Á,Â,Ã,Ä,Å,Æ,à,á,â,ã,ä,å,æ

 

三、校對規則導致的問題

1、混合校對規則比較

兩個字符串比較,要求兩者必須有相同的校對規則,或者兩者的校對規則是相容的——所謂相容是指,兩種校對規則優先級不同,比較的時候兩者使用高優先級的校對規則進行比較,比如latin1_bin的優先級相對較高。

CREATE TABLE `tbl` (
  `col_a` int(11) default NULL,
  `col_b` char(20) character set latin1 collate latin1_general_ci default NULL,
  `col_c` char(20) character set latin1 collate latin1_german1_ci default NULL,
  `col_d` char(20) character set latin1 collate latin1_bin default NULL,
  KEY `col_a` (`col_a`),
  KEY `col_b` (`col_b`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

在這個表中,col_b、col_c、col_d的校對規則各不同;其中,latin1_general_ci和latin1_german1_ci 校對規則同級,不能進行比較;如果強行比較的話,就會報錯,如下:

mysql> select * from tbl where col_b = col_c;
ERROR 1267 (HY000): Illegal mix of collations (latin1_general_ci,IMPLICIT) and (latin1_german1_ci,IMPLICIT) for operation '='

而latin1_general_ci和latin1_bin的優先級不同,latin1_bin高於latin1_general,因此比較的時候,會按照latin1_bin的規則進行比較。

mysql> select * from tbl where binary col_b = col_d;
Empty set (0.00 sec)

當然,可以在sql語句中強制指定校對規則進行比較,下面這個例子就說明了這一點:

mysql> select * from tbl where col_b COLLATE latin1_danish_ci = col_c COLLATE latin1_danish_ci;
Empty set (0.00 sec)

 

2、校對規則導致的問題——SELECT出錯誤的記錄

 在上面的基礎上,我們要演示一個常見的問題;我們需要對該數據表進行一定的處理:

alter table tbl modify col_b collate latin1_swedish_ci default null;
insert into tbl (col_b) values ('hao123');

然后進行下面的查詢

我們希望查詢的是包含“劉”的記錄,hao123這個和“劉”沒有任何關系的條目被選了出來,看起來很奇怪。
不過這不是數據庫出了問題,而是校對規則的使用上存在問題:
下面是我們使用ultraedit察看字符串的二進制編碼的結果,在gbk編碼下,hao123的編碼為68 61 6f 31 32 33,而劉的編碼位C1 F5。

在前面的latin1_swedish_ci 校對規則中可以看到:

61和C1都與41等價

6F和F5都與4F等價

這就是ao = 劉的原因。

解決辦法有兩個:
1)修改該字段的字符集和校對規則,改成gbk,這該問題不在存在。這是完美的解決方案,不過有些時候你沒有權限對數據庫進行這樣的改動。

mysql> alter table tbl modify col_b char(20) charset gbk default null;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from tbl where binary col_b like like '%劉%';
Empty set (0.00 sec)

2)查詢的時候聲明校對規則為latin1_bin 。這樣可以在一定程度上緩解這個問題;不過如果col_b中只要含有c1 f5,就會被選出來——而c1 f5可能恰好是另外兩個字符的前半截和后半截,或者干脆就是 Á õ ....

mysql> select * from tbl where binary col_b like '%劉%';
Empty set (0.00 sec)

 

 

轉載:http://hi.baidu.com/cuttinger/item/e23013e372ee62adce2d4fda


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM