现在的位置: 首页Mysql>正文
解决mysql gbk/gb2312时Y和~重复的问题
2012年05月18日 Mysql 评论数 1 ⁄ 被围观 9,730 次+

最近整理论坛把原来的mysql数据库校对规则由gbk_bin换成gbk_chinese_ci后出现了Y与~重复的问题。不知道mysql官方的bugs里面安排到什么时候解决这个bug,下面说明一下具体的原因。

1、问题描述

  1. mysqlCREATE TABLE `test` (`namechar(32) DEFAULT NULL) ENGINE=MyISAM DEFAULT CHARSET=gbk CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC;   
  2. Query OK, 0 rows affected (0.00 sec)   
  3.   
  4. mysql> insert into test values('YYY');   
  5. Query OK, 1 row affected (0.00 sec)   
  6.   
  7. mysql> insert into test values('~~~');   
  8. Query OK, 1 row affected (0.00 sec)   
  9.   
  10. mysql> select * from test where name='YYY';   
  11. +------+   
  12. name |   
  13. +------+   
  14. | YYY  |   
  15. | ~~~  |   
  16. +------+   
  17. rows in set (0.00 sec)  

解决mysql gbk/gb2312时Y和~重复的问题

上面的语句和显示结果不难看出,where name='YYY'的条件匹配到了 '~~~'

2、修改mysql源代码
解压缩mysql源码包,打开strings/ctype-gbk.c,查看sort_order_gbk这个结构体,这里是gbk的码表。可以看到第158行为” 'X', 'Y', 'Z', '{', '|', '}', 'Y', '\177',”. 这里第二个’Y’明显错误, 将其改为’~’,重新编译,执行新的mysqld,在此声明一下,我的测试环境版本为mysql-5.1.63
如果你要改gb2312的话修改ctype-gb2312.c即可,大概在143行。
再次测试上例结果如下图所示:

  1. mysql> CREATE TABLE `test` (`namechar(32) DEFAULT NULL) ENGINE=MyISAM DEFAULT CHARSET=gbk CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC;   
  2. Query OK, 0 rows affected (0.00 sec)   
  3.   
  4. mysql> insert into test values('YYY');   
  5. Query OK, 1 row affected (0.00 sec)   
  6.   
  7. mysql> insert into test values('~~~');   
  8. Query OK, 1 row affected (0.00 sec)   
  9.   
  10. mysql> select * from test where name='YYY';   
  11. +------+   
  12. name |   
  13. +------+   
  14. | YYY  |   
  15. +------+   
  16. rows in set (0.00 sec)  

解决mysql gbk/gb2312时Y和~重复的问题

3、原因说明
我们来分析一下strings/ctype-gbk.c的这个函数my_strnncoll_gbk_internal。当使用gbk字符时,在作字符串匹配的时候,调用此函数。

  1. int my_strnncoll_gbk_internal(const uchar **a_res, const uchar **b_res,   
  2.                   size_t length)   
  3. {   
  4.   const uchar *a= *a_res, *b= *b_res;   
  5.   uint a_char,b_char;    
  6.   
  7.   while (length--)   
  8.   {   
  9.     if ((length > 0) && isgbkcode(*a,*(a+1)) && isgbkcode(*b, *(b+1)))   
  10.     {   
  11.       a_char= gbkcode(*a,*(a+1));   
  12.       b_char= gbkcode(*b,*(b+1));   
  13.       if (a_char != b_char)   
  14.         return ((int) gbksortorder((uint16) a_char) -   
  15.         (int) gbksortorder((uint16) b_char));   
  16.       a+= 2;   
  17.       b+= 2;   
  18.       length--;   
  19.     }   
  20.     else if (sort_order_gbk[*a++] != sort_order_gbk[*b++])   
  21.       return ((int) sort_order_gbk[a[-1]] -   
  22.           (int) sort_order_gbk[b[-1]]);   
  23.   }   
  24.   *a_res= a;   
  25.   *b_res= b;   
  26.   return 0;   
  27. }  

第9行是作中文判断的,先忽略。在我们的例子中,走的是第20行的判断。
可以看到,在判断字符值是否相同时使用sort_order_gbk[*a++] != sort_order_gbk[*b++]。当我们的*a='~', *b='Y'时,由于上面说到的158行的原因,被判定为值相同。
(作为辅助验证,如果有兴趣把第二个'Y' 改成 'Z',以后Z和~就相同了)。

4、其他解决方法
如果不改源码,也有"解决方法"
A、使用binary查询
使用binary查询后,整个字符串对照流程都与gbk无关,因此不会碰到这个bug。副作用是导致大小写敏感。
B、换字符集
从strings/目录下的各个ctype-*.c看, 只有gbk和gb2312有这个问题,因此改成utf-8,自然没有这个问题。副作用是改变了原有数据长度,且涉及到重做表。

本文地址:http://www.92csz.com/28/1168.html
如非注明则为本站原创文章,欢迎转载。转载请注明转载自:moon's blog
 

目前有 1 条留言 其中:访客:1 条, 博主:0 条

  1. 重生红三代 : 2012年05月25日14:41:28  -49楼

    谢谢博主分享。顶你一个。