MySQL字符集问题

遇到了这么个问题,数据库表的字符集是utf8,但客户端程序在建立连接时设置了:set names latin1。
查询数据库中的中文字符“尊”的列,返回的是”尚“。
“尊”和“尚”被错误的认为是相等的。

要解释上边的怪现象需要先明确以下几个概念:

  • MySQL Charset(字符集)
  • MySQL Collate(校对集)
  • character_set_client
  • character_set_connection
  • character_set_results

需要知道这三个函数

  • hex()
  • unhex()
  • convert()

这几个概念网上的资料很多,在这里就不废话了。

需要特别说明下,character_set_client是客户端传输数据到服务器端使用的字符集。
character_set_connection是服务器端在处理文本时默认使用的字符集。这个character_set_connection因为名字用了connection,比较容易产生误解,我一开始就误解成了客户端和服务器端建立连接使用的字符集。

假设set names utf8,此时character_set_connection的值将被设置为utf8.
执行 select‘尊’;
相当于 select (_utf8 ‘尊’)
(在这里我们使用一个比较特殊的符号“_utf8”,它叫做“引介词”,可参考资料)

单独的一个select并不能说明问题,我们对字符串进行一下比较,执行以下SQL:
select * from test where name=’尊’
假设列test.name的字符集是utf8,此时字符串’尊’的字符集也是utf8,将直接进行比较,不需要进行字符集转换。
此时的SQL相当于:
select * from test where name = (_utf8 ‘尊’)

如果set names gbk后再次运行以上SQL,此时test.name的字符集仍然是utf8,但‘尊’的字符集变成了gbk,等价SQL如下:
select * from test where name = (_gbk ‘尊’)
在进行字符串比较时会进行隐式的字符集转换。
关于字符集比较和隐式转换可参考资料(MySQL Manual - Collation of Expressions

理解了以上几个概念,我们解释下文章开头的怪现象。
由于设置了set names latin1,字符从客户端传到服务器端使用的是latin1字符集
由于数据库表中的字符集是utf8,在进行比较的时候会再次将latin1的字符隐式转换成utf8。

以上比较绕的整个过程可以简化为如下一条SQL:
select convert(_latin1’尊’ using utf8)
(将’尊’先转化为latin1编码,再转换为utf8编码)

前期存到数据库的汉字“尚”也是这样一个转换过程,使用如下SQL检验:
select convert(_latin1’尊’ using utf8) = convert(_latin1’尚’ using utf8);
返回结果为相等。

以上实验只是重现了相等的过程,并没有解释为什么会相等。
这牵扯latin1编码的问题。Latin1是单字节编码,不认识汉字,但将汉字转换成latin1编码的时候将保留原来的编码格式直接存储。如汉字“尊”和“尚”的编码为:
select hex(‘尊’) => E5B08A
select hex(‘尚’) => E5B09A
这两个汉字被编码为3个字节,这三个字节在latin1字符集里对应的字母是:å°Š和å°š
这两个字符串å°Š和å°š在UTF8的默认比较规则下相等。

查看’尊’被转换后成为的十六进制:
select hex(convert(_latin1’尊’ using utf8)) => C3A5C2B0C5A0
查看’尊’被转换后对应的字符:
select unhex(hex(convert(_latin1’尊’ using utf8))) => å°Š

OK,解释完毕。
总结如下:
不能瞎用latin1连接去查询或操作utf8编码的数据库数据。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值