设置MySQL客户端连接使用的字符集

时间:2014-03-05    来源:服务器之家    投稿:root

考虑什么是一个“连接”:它是连接服务器时所作的事情。客户端发送SQL语句如查询,通过连接发送到服务器,服务器通过连接发送响应给客户端如结果集。
    对于客户端连接,这样会导致一些关于连接的字符集和校对规则的问题,这些问题均能够通过系统变量来解决:
    i   当查询离开客户端后,在查询中使用哪种字符集?
    character_set_client变量“声明告知”服务器客户端的字符集是什么,它和字符串文字的引介词起着同样的作用。注意,是“声明”,故实际上有可能不是,即客户端可以用这个“声明”来欺骗服务器,从而“隐藏”自己真正的字符集。
    ii  服务器接收到查询后应该转换为哪种字符集?
    当服务器接到客户端发来的语句后,它会将“这串字符”做一层字符编码的映射转换。它会把字符串从character_set_client字符集转换到character_set_connection字符集(除非字符串文字具有象_latin1或_utf8的引介词)。
    iii 服务器发送结果集或返回错误信息到客户端之前应该转换为哪种字符集?
    character_set_results变量指示服务器返回查询结果到客户端使用的字符集。包括结果数据,例如列值和结果元数据(如列名)。
    iv  字符串最后存储是哪种字符集?
    字符串最终保存下来是什么字符集,这个是分别受到server、db、table和column四个级别的定义控制的,它们决定了字符串最终以什么字符集存储。
    v   不同字符集之间会发生转换吗?
    当字符串在不同字符集之间“穿梭”时,会发生字符集编码的映射转换,如果发生相互转换的两种字符集不是相互兼容的,那么字符串就有可能会发生丢失。

mysql server中,下面的两个语句影响连接字符集:
SET NAMES 'charset_name';
SET CHARACTER SET charset_name;
    SET NAMES显示客户端发送的SQL语句中使用什么字符集。因此SET NAMES 'cp1251'语句告诉服务器“将来从这个客户端传来的信息采用字符集cp1251”。它还为服务器发送回客户端的结果指定了字符集(如你使用一个SELECT语句,它表示列值使用了什么字符集)。SET NAMES 'x'语句与这三个语句等价:
mysql> SET character_set_client = x;
mysql> SET character_set_results = x;
mysql> SET character_set_connection = x;
将x设置为character_set_connection也就设置了collation_connection是x的默认校对规则。
    SET CHARACTER SET语句是类    似的,但是为默认数据库设置连接字符集和校对规则。SET CHARACTER SET x语句与这三个语句等价:

mysql> SET character_set_client = x;
mysql> SET character_set_results = x;
mysql> SET collation_connection = @@collation_database;
    当一个客户端连接时,它向服务器发送希望使用的字符集名称,服务器把变量character_set_client、character_set_results和character_set_connection设置为该字符集,即实际上服务器为使用该字符集执行一个SET NAMES操作。

对于mysql客户端,如果你希望使用与默认字符集不同的字符集,不需要每次启动时执行SET NAMES语句,那么可以在mysql语句行中或者选项文件中添加一个--default-character-set选项设置。例如,你每次运行mysql时,以下的选项文件设置把三个字符集变量修改为koi8r:
[mysql]
default-character-set=koi8r
例如:假设column1定义为CHAR(5) CHARACTER SET latin2。如果没有设定SET NAMES或SET CHARACTER SET,那么对于SELECT column1 FROM t,当连接后,服务器使用客户端指定的字符集(即character_set_client声明的字符集)返回列column1的所有值。另一方面,如果你设定SET NAMES 'latin1'或SET CHARACTER SET latin1,那么发送结果之前,服务器转换latin2值到latin1。转换可能会丢失那些不属于两种字符集的字符。如果不希望服务器执行任何转换,设置character_set_results为NULL:
mysql> SET character_set_results = NULL;

下面我来做实验来证明上面所说的内容。
    首先,用chcp命令可以查询到自己的cmd客户端是什么编码格式:
C:\Documents and Settings\shengtong>chcp
活动的代码页: 936  -- 936就是表示gbk的编码的意思
    然后,在gbk编码对照表中,查询出汉字“中”的编码是D6D0。至此,已经清楚cmd客户端的字符集和汉字“中”gbk编码了。
 
    第一个实验,我们保证character_set_client变量和character_set_connect变量一样,即保证声明的客户端和服务器是一样的编码,这样字符串传到服务器的时候就不需要转换,然后将字符串存储下来。
mysql> create table t (a varchar(100),b varchar(20),c int) default character set=gbk;  -- 创建一个最终以gbk字符集存储数据的表t
Query OK, 0 rows affected (0.06 sec)
   
mysql> set names 'gbk';  -- 在服务器中“声明告知”客户端的字符集的“实情”,即character_set_client变量通过set names设置为gbk
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values('中','gbk',1);
Query OK, 1 row affected (0.05 sec)

mysql> select a,b,c,hex(a),hex('中') from t;
+------+------+------+--------+-----------+
| a    | b    | c    | hex(a) | hex('中') |   -- hex(a)列显示了表字段a的存储格式,hex('中')显示了gbk编码格式
+------+------+------+--------+-----------+
| 中   | gbk  |    1 | D6D0   | D6D0      |
+------+------+------+--------+-----------+
1 row in set (0.00 sec)

这里可以清楚的看到表t正确的存储了汉字“中”,同时也表名了“中”的gbk编码为D6D0。解释一下这个过程,客户端字符集是gbk,然后其通过set names想server声明其字符集为gbk,并且底层表的字符集也是gbk,故一路下来在字符串的穿梭中没有发生任何字符集的转换,其最终在表t中正确的存储了汉字中,并且是正确的编码D6D0。查询时,同样字符串从存储引擎到server到客户端的传递游离中并没有发生字符集的转换,一切都可以正确显示出来。

mysql> set names 'latin1';  -- 对服务器“隐瞒”客户端字符集的“真实面目”,声明了一个错误的character_set_client变量为latin1
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values('中','latin1',2);
Query OK, 1 row affected, 1 warning (0.03 sec)

mysql> show warnings;
+---------+------+------------------------------------------------------------+
| Level   | Code | Message                                                    |
+---------+------+------------------------------------------------------------+
| Warning | 1366 | Incorrect string value: '\xD6\xD0' for column 'a' at row 1 |
+---------+------+------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select a,b,c,hex(a),hex('中') from t;
+------+--------+------+--------+-----------+
| a    | b      | c    | hex(a) | hex('中') |
+------+--------+------+--------+-----------+
| ?    | gbk    |    1 | D6D0   | D6D0      |
| ??   | latin1 |    2 | 3F3F   | D6D0      |
+------+--------+------+--------+-----------+
2 rows in set (0.00 sec)

mysql> create table t1 ( a varchar(10)) default character set=utf8;
Query OK, 0 rows affected (0.06 sec)

mysql> set names latin1;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1 values('中');
Query OK, 1 row affected (0.00 sec)

通过SET NAMES,客户端向服务器声明它使用的是latin1字符集,但实际上它欺骗了它,它实际上上是gbk字符集。由于character_set_client和character_set_connection两个变量相同,故在server中他们是不需要转换的,故hex('中')的值仍然是D6D0。由于表的字符集是gbk,故当存储数据的时候就会发生一次字符集的编码映射转换。转换的过程是这样的,由于latin1是单字节编码字符集,那么传过来的两个字节0xD6和0xD0,会根据其在latin1中对应的“字”,然后转到相应的gbk编码的“字”,然后根据这个gbk字编码存储起来。在转换发生时,发现latin1字符集中的0xD6和0xD0在gbk找不到对应的字,故报warnings,无法完成转换,那么就只有插入个无意义的“字”,故就用gbk编码的无意义字0x3F存储了起来(?的编码在所有字符集中都是0x3F,而且对某个字符集不能表示的编码所对应的字符就用?)。但是在utf8的编码表中latin1编码为D6和D0的“字”能够正确表示,故能将他们存储起来。
    解释一下,为什么不能正确查看c=1的这行记录。因为这里通过set names已经将character_set_results设置为latin1了,故数据从表中出来以后,会做一层转换,从gbk转到latin1。转换时,发现“中”在latin1中无法显示,故以0x3F编码的?来显示了。
    接下来接着验证一下latin1和gbk中0xD6和0xD0是什么字符,?的编码等:
mysql> select _latin1 x'D6';
+---------------+
| _latin1 x'D6' |
+---------------+
| ?             |
+---------------+
1 row in set (0.01 sec)

mysql> select _latin1 x'D0';
+---------------+
| _latin1 x'D0' |
+---------------+
| ?             |
+---------------+
1 row in set (0.00 sec)

mysql> set names 'gbk';
Query OK, 0 rows affected (0.00 sec)

mysql> select hex('?');
+----------+
| hex('?') |
+----------+
| 3F       |
+----------+
1 row in set (0.03 sec)

mysql> set names 'latin1';
Query OK, 0 rows affected (0.00 sec)

mysql> select hex('?');
+----------+
| hex('?') |
+----------+
| 3F       |
+----------+
1 row in set (0.00 sec)

以上转换过程,看看在变成字节编码的utf8中是不是也是根据我们所阐述的发生着转换。在utf8中,汉字的编码都是3个字节编码的:
mysql> set names 'utf8';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values('中','utf8',3);
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+------------------------------------------------------------+
| Level   | Code | Message                                                    |
+---------+------+------------------------------------------------------------+
| Warning | 1366 | Incorrect string value: '\xD6\xD0' for column 'a' at row 1 |
+---------+------+------------------------------------------------------------+
1 row in set (0.14 sec)

mysql> select a,b,c,hex(a),hex('中') from t;
+------+--------+------+--------+-----------+
| a    | b      | c    | hex(a) | hex('中') |
+------+--------+------+--------+-----------+
| 涓? | gbk    |    1 | D6D0   | D6D0      |
| ??   | latin1 |    2 | 3F3F   | D6D0      |
| ?    | utf8   |    3 | 3F     | D6D0      |
+------+--------+------+--------+-----------+
3 rows in set (0.00 sec)

mysql> select _utf8 x'D6D0D3';   -- 这里说明0xD6D0在utf8中是无法显示的编码,故其最终以字符“?”代替,且就一个,那么存到gbk编码中就说一个0x3f编码的?了。
ERROR 1300 (HY000): Invalid utf8 character string: 'D6D0D3'

上面这个实验已经很好的说明了mysql中字符集之间的映射转换过程,整个实验是保证character_set_client和character_set_connection、character_set_results一样来完成的,那么如果他们不一样呢?
    请看实验二:
mysql> set names 'gbk';
Query OK, 0 rows affected (0.00 sec)

mysql> select hex('中');
+-----------+
| hex('中') |
+-----------+
| D6D0      |
+-----------+
1 row in set (0.00 sec)

mysql> set character_set_client=latin1;
Query OK, 0 rows affected (0.00 sec)

mysql> set character_set_connection=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> set character_set_results=gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> select hex('中');
+-----------+
| hex('??') |
+-----------+
| C396C390  |
+-----------+
1 row in set (0.02 sec)

mysql> set character_set_results=latin1;
Query OK, 0 rows affected (0.00 sec)

mysql> select hex('中');
+-----------+
| hex('中') |
+-----------+
| C396C390  |
+-----------+
1 row in set (0.00 sec)

latin1编码为0xd6和0xd0对应的utf8编码为0xc396和0xc390,client到connection完成了正确的转换,而hex函数返回的结果都是英文字母和数字,故不论results设置成什么,结果都是c396c390。

实验三,再来看看character_set_results参与的转换:
mysql> set character_set_client=latin1;
Query OK, 0 rows affected (0.00 sec)

mysql> set character_set_connection=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> set character_set_results=latin1;
Query OK, 0 rows affected (0.00 sec)

mysql> select '中';
+----+
| 中 |
+----+
| 中 |
+----+
1 row in set (0.00 sec)

latin1编码的0xD6D0从client被转换到connection的utf8编码,结果为0xC396C390,返回时,又从0xC396C390转换到0xD6D0。而0xD6D0,刚好能正确显示汉字“中”。

mysql> set character_set_results=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> select '中';
+------+
| 脰脨 |
+------+
| 脰脨 |
+------+
1 row in set (0.00 sec)

由于connection和results是一样的,故不转换,那么汉字编码为0xC396C390的汉字“脰脨”就被显示了出来。

mysql> set character_set_results=gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> select '中';
+----+
| ?? |
+----+
| ?? |
+----+
1 row in set (0.00 sec)

utf8编码为0xC396C390的字符在gbk中没有对应的“字”,故connection到results的转换不成功,以乱码表示了。

mysql> set character_set_results=null;
Query OK, 0 rows affected (0.00 sec)

mysql> select '中';
+------+
| 脰脨 |
+------+
| 脰脨 |
+------+
1 row in set (0.00 sec)

屏蔽了connection到results的转换,故最终显示gbk编码为0xC396C390的两个字“脰脨”

设置MySQL客户端连接使用的字符集的更多相关文章

  1. PostgreSQL数据库服务端监听设置及客户端连接方法教程

    众所周知,PostgreSQL 是一个自由的对象-关系数据库服务器(数据库管理系统),是一个可以免费使用的开放源代码数据库系统.本文详细介绍了PostgreSQL数据库服务端监听设置及客户端连接方法, ...

  2. win10的MySQL客户端连接centos7虚拟机的mysql服务端连接不上解决办法

    分别在win10和Centos虚拟机上装有MYSQL,但用电脑的mysql客户端连接centos7的服务端报错如下: 解决办法: 如果可以从虚拟机连接到电脑的MYSQL服务端, 那就是CentOS7的 ...

  3. mac 设置 MySQL 数据库默认编码(字符集)为 UTF-8

    mac 设置 MySQL 数据库默认编码(字符集)为 UTF-8   原文链接:https://juejin.im/post/5bbdca76e51d45021147de44 鉴于有些刚接触 MySQ ...

  4. 解决mysql 客户端连接问题

        问题:在linux 上安装了mysql服务端,使用客户端连接时报错信息为:ERROR 1045 (28000): Access denied for user 'root'@'localhos ...

  5. Ubuntu Server 设置PPTP客户端连接

    安装PPTP客户端 apt-get install pptp-linux 设置连接账号信息 sudo vim /etc/ppp/chap-secrets 其中$login_name是登录名:$pass ...

  6. MySQL客户端连接方式

    MySQL连接方式MySQL除了最常见的TCP连接方式外,还提供SOCKET(LINUX默认连接方式).PIPE和SHARED MEMORY连接方式.各连接方式的服务器.客户端启动选项,及连接默认值见 ...

  7. MySQL skip-character-set-client-handshake导致的一个字符集问题

    http://www.quweiji.com/mysql-skip-character-set-client-handshake%E5%AF%BC%E8%87%B4%E7%9A%84%E4%B8%80 ...

  8. Mysql从客户端连接服务器连不上的问题

    Mysql从客户端连接服务器连不上的问题   公司要用Mysql做一个测试,开始在自己的本地建一个Mysql数据库自己本地的程序再连上去,没有遇到过连接不上的问题.这次数据库在服务器上,从本地客户端连 ...

  9. 安装 mysql 8.0后;root用户在客户端连接不上

    --- mysql workbench Failed to Connect to MySQL at 10.211.55.6:3306 with user root Authentication plu ...

随机推荐

  1. css3的伪(伪类和伪元素)大合集

    本文讲css3的伪,不是讲它有多虚伪,而是说它的伪元素样式.不得不说以前虽知html伪元素,但很少用,后得知借助css3伪元素可以发挥极大的便利.故总结css3的伪如下: CSS中存在一些比较特殊的属 ...

  2. Essay写作:Conclusion部分写作辅导

    论文写到最后,一般正文就要以Conclusion结束了.Conclusion部分是一篇论文的正文结尾(the last section of a paper,last paragraph),主要是客观 ...

  3. bean的autowire属性及其生命周期

    一:sutowire属性 1.no:默认值,禁用自动装配: 2.byName:根据属性名称自动装配: 3.byType:根据属性类型自动装配: 4.constructor:通过构造方法自动装配,不推荐 ...

  4. python数据分析基础

    ---恢复内容开始--- Python数据分析基础(1) //2019.07.09python数据分析基础总结1.python数据分析主要使用IDE是Pycharm和Anaconda,最为常用和方便的 ...

  5. SDRAM调试总结

    SDRAM的调试总结 1 说明 实验平台: JZ2440 CPU: S3C2440 SDRAM型号: EM63A165TS-6G   2 SDRAM的一些基本概念 2.1 引脚分配   2.2 引脚描 ...

  6. [强网杯 2019]Upload

    0x00 知识点 代码审计,PHP 反序列化. 0x01 解题 先注册一个账号,再登陆 上传 简单测试一下: 只能上传能被正常查看的 png. F12看到文件上传路径 扫扫敏感文件 存在:/www.t ...

  7. GNS3 模拟icmp分片不可达

    R1 : conf t int f0/0 no shutdown ip add 192.168.1.1 255.255.255.0 no ip routing end R2 f0/0: conf t ...

  8. swift中数据之间的转换

    1.swift 开发 - NSDictionary与NSData互转.Dictionary与Data互转 https://blog.csdn.net/SuperMageHuang/article/de ...

  9. 吴裕雄--天生自然C++语言学习笔记:C++ 模板

    模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码. 模板是创建泛型类或函数的蓝图或公式.库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念. 每个容器都有一个单 ...

  10. composer install、require、update的区别