专治 MySQL 乱码,再也不想看到乱码了( 二 )


读取时,MySQL会将目标表格中的数据转化为character_set_results指定的编码 。由于我们写入时使用的Latin-1,读取时也需要指定character_set_results为Latin-1 。这样最终就实现了“错进错出” 。
举个例子假设有这样一张student表:
|name| age|
|----|----|
|小明|12|
|小红|10|
其中,name列编码为Latin-1,其储存的数据使用的编码为GBK 。
也就是说向表里存入数据的人可能使用GBK的终端下执行了下列语句:
SET NAMES latin1;
INSERT INTO student VALUES ('小明', 12);
那么,如果我们现在使用的终端编码为UTF-8,要怎样从表中查询关于小明的信息呢?

  1. 可以尝试直接登陆MySQL,输入以下语句:
SELECT * FROM student WHERE name = "小明";
但这样做得到了一个错误:
ERROR 1267 (HY000): Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '='
MySQL默认用户终端使用的是UTF-8编码,与表格的编码 Latin-1 不一致,于是MySQL会首先尝试把查询语句转换为Latin-1 。但是Latin-1中没有对应“小明”这两个字的编码,因此会报错 。
  1. 如果增加一条改变character_set_client的语句,会怎么样呢?
SET NAMES latin1;
SELECT * FROM students WHERE name = "小明";
这一次MySQL会认为用户的终端就是Latin-1编码,所以没有做转换操作 。但最终查询到的结果却为空 。
这是因为用户终端的编码是UTF-8, 因此传入的“小明”的编码也是UTF-8,而表格中的数据是GBK编码,它们在内存中的储存形式不同 。因此,即便MySQL都将它们当作Latin-1处理,也不会认为它们相等 。
  1. 不直接登陆MySQL,而是在Shell中先将查询语句转化为GBK编码,再传入MySQL:
echo "
SET names latin1;
SELECT * FROM student WHERE name = '小明';"
| iconv -f utf8 -t gbk
| mysql -uroot -p123 -Dtest
其中iconv的作用是将标准输入转换为指定的编码格式(这里是GBK),再通过标准输出传递给MySQL 。我们得到了:
name age
С?? 12
能查询到结果,但名字部分是乱码 。这是由于表格中储存的数据是GBK编码,而终端编码是UTF-8 。所以还需要增加最后一步:将查询的结果转换为UTF-8 。
echo "
SET names latin1;
SELECT * FROM student WHERE name = '小明';"
| iconv -f utf8 -t gbk
| mysql -uroot -p123 -Dtest
| iconv -f gbk -t utf8
输出结果为:
name age
小明 12
这样,我们终于得到了正确的信息 。
如果表格本身就是GBK编码,而不是Latin-1,是否还需要这样的繁琐的步骤呢?
答案是不需要的 。因为只要正确地设置了character_set_client和character_set_results,尽管表格的编码是GBK,MySQL在读写的过程中会自动进行转换 。




推荐阅读