MYSQL校對規則

1、前言

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

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

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

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

上面的場景說明了校對規則在最簡單狀況下起的做用;實際狀況與此並無太多不一樣,只不過稍微有些複雜而已。mysql

 

2、校對規則總覽

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

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

舉個例子,在latin1_german1_ci中,以下字符是等價的,而他們,具備不一樣的外形和編碼。固然,它們的編碼不一樣,因此在latin1_bin校對規則下,他們又是不等價的了。
A,a,À,Á,Â,Ã,Ä,Å,Æ,à,á,â,ã,ä,å,æ數據庫

 

3、校對規則致使的問題編碼

一、混合校對規則比較spa

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

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 校對規則同級,不能進行比較;若是強行比較的話,就會報錯,以下:blog

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語句中強制指定校對規則進行比較,下面這個例子就說明了這一點:ci

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

 

二、校對規則致使的問題——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

相關文章
相關標籤/搜索