五、字符集操作示例
通过一个最简单的示例:“向某测试表插入记录,确保写入的记录能被正确地保存,并能被正常地读取”。
先来创建一个测试表13,拥有3个列,并为每个列分别指定不同的字符集,所指定的3种字符集,也是目前MySQL环境最为常见的字符集,执行操作如下:
([email protected])[leedb]> use leedb;
Database changed
([email protected])[leedb]>
([email protected])[leedb]>
([email protected])[leedb]> create table t3(
-> v1 varchar(20) charset latin1,
-> v2 varchar(20) charset gbk,
-> v3 varchar(20) charset utf8
-> );
Query OK, 0 rows affected (0.05 sec)
设置当前客户端连接会话的字符集为utf8:
([email protected])[leedb]> set names utf8;
Query OK, 0 rows affected (0.00 sec)
([email protected])[leedb]> show variables like ‘character%‘;
+--------------------------+----------------------------------+
| Variable_name | Value |
+--------------------------+----------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /usr/local/mysql/share/charsets/ |
+--------------------------+----------------------------------+
8 rows in set (0.01 sec)
向这个表中插入一条记录,每个列都指定相同的值,中英文字符混合:
([email protected])[leedb]> insert into t3 values (‘cn 中国‘,‘cn 中国‘,‘cn 中国‘);
ERROR 1366 (HY000): Incorrect string value: ‘\xD6\xD0\xB9\xFA‘ for column ‘v1‘ at row 1
返回1366号错误信息,细看提示就能明白,原来在提示vl列指定了错误的字符串值,导致插入失败。这个错误提示可以理解,因为前面说过,latinl是西文字符集,并不支持中文这样的多字节字符,因此插入时就报错了。
不过,考虑到MySQL 5.6正式版本刚出不久,大部分用户仍在使用5.6之前的版本, 需要提醒大家的是,在MySQL 5.6之前版本,1366号信息并非错误,而是一条警告,也就是说在之前版本中,出现这种情况只会抛出一条警告,但插入操作仍能成功。但是,都出现警告 了,实际插入的数据出现异常的概率也就相当的高了,若是以为插入成功,但查询时才发现信息错误,那就麻烦了。因此这就需要大家更加慎重地对待,因为有时候 操作失败报错,要比操作(同样)失败报警更合理一些呐。进入5.6版本后,MySQL修改1366号的错误级别。
提示:此处1366错误级别由系统变量sql_mode来控制
sql_mode系统变量,顾名思义用来控制SQL模式,MySQL提供的模式众多,不同模式提供了不同的功能或限制条件。在MySQL 5.6版本之前,sql_mode默认为空,而进入到5.6版本后, Sql_mode、默认值改为STRICT_TRANS_TABLES,这个变量值的功能是对于支持事务的存储引擎对象,启动严格限制模式,这种情况下,就会出现插入值非法则直接报错,而非警告。
若仍然希望像5.6版本之前,插入字符串值不匹配抛出警告而非错误,则将sql_mode值改为空,如果该系统变量拥有多个值,则去除STRICT_TRANS_TABLES值即可。
下面修改一下col1待插入的值,而后再次执行INSERT语句:
([email protected])[leedb]> insert into t3 values (‘china‘,‘cn 中国‘,‘cn 中国‘);
ERROR 1366 (HY000): Incorrect string value: ‘\xD6\xD0\xB9\xFA‘ for column ‘v2‘ at row 1
又报了几乎一模一样的错误,不过这回变成v2列字符串,即使我们继续尝试,把v2列值改一下,再次尝试插入时又会抛出错误,提示v3列的字符有误。
vl这种latinl编码的列,插入中文字符出现错误还可以理解,因为它确实不支持中文编码,但为什么v2和v3这两列也会是乱码呢?这两个一个使用gbk编码,一个基于utf8编码,应该是都能够支持中文字符的呀!
既然存储阶段的字符集设置没有问题,我们输入的字符也没有问题,那就只有一个可能:连接阶段的字符集编码不对。可是,回想一下,在操作之前,我们己经显式地指定了当前连接会话的字符集为utf8了呀,utf8能够支持中文的不是嘛?
utf8编码能够支持中文不假,但是,utf8编码格式下的“中国”,就是我们输入的“中国”这两个字符呢?这明明是gb2312或者说gbk编码的字符形式。
插入时报错,不是我们输入的不对,或者表列的字符集设置不对,而是客户端的字符集和存储所用字符集不匹配的结果。通过实际操作验证一下,先把当前客户端会话的字符集改为gbk,然后再执行相同的INSERT操作看一看:
([email protected])[leedb]> set names gbk;
Query OK, 0 rows affected (0.00 sec)
([email protected])[leedb]> insert into t3 values (‘china‘,‘cn 中国‘,‘cn 中国‘);
Query OK, 1 row affected (0.01 sec)
([email protected])[leedb]> select * from t3;
+-------+---------+---------+
| v1 | v2 | v3 |
+-------+---------+---------+
| china | cn 中国 | cn 中国 |
+-------+---------+---------+
1 row in set (0.00 sec)
可以看到,连接时字符集和存储时的字符集一致,结果才能正常显示。
那么utf8字符集的v3列为什么也能正常显示?就是character_result系统变量的功劳了嘛,它被转换成了 gbk字符集形式.
再把连接会话的字符集改为utf8,看看表中的记录又会变成什么样呢
([email protected])[leedb]> set names utf8;
Query OK, 0 rows affected (0.00 sec)
([email protected])[leedb]> select v1,v2,v3,length(v1),length(v2),length(v3) from t3;
+-------+-----------+-----------+------------+------------+------------+
| v1 | v2 | v3 | length(v1) | length(v2) | length(v3) |
+-------+-----------+-----------+------------+------------+------------+
| china | cn 涓浗 | cn 涓浗 | 5 | 7 | 9 |
+-------+-----------+-----------+------------+------------+------------+
1 row in set (0.00 sec)
验证:
([email protected])[leedb]> insert into t3 values(‘china‘,‘cn 涓浗‘,‘cn 涓浗‘);
Query OK, 1 row affected (0.04 sec)
([email protected])[leedb]> set names gbk;
Query OK, 0 rows affected (0.00 sec)
([email protected])[leedb]> select * from t3;
+-------+---------+---------+
| v1 | v2 | v3 |
+-------+---------+---------+
| china | cn 中国 | cn 中国 |
| china | cn 中国 | cn 中国 |
+-------+---------+---------+
2 rows in set (0.00 sec)
可以看到,UTF8编码中的“cn 涓浗”正是GBK编码的“中国”。
六、字符集设置
1、字符串的字符集
字符集是针对MySQL数据库中的字符类型,我们一般设置字符集,都是对存储对象进行设置,这里还有一个很细节的问题,对于那些并非保存在数据库对象里的字符是否拥有字符集呢?比如下面这个语句:
select ‘cn 中国‘;
这个语句中的字符串有字符集吗?这点毋庸置疑,字符类型都有字符集。不过出现在SQL语句中某个角落的字符串,多数情况下我们都是理所当然地就用了,它们的字符集却被我们忽视。
针对字符串,用户可以在使用时, 通过相应语句来显式地设置字符集及校对规则,其基础语法如下:
[_charset_name]‘string‘ [COLLATE collation_name]
这其中:
[_charset_name]:指定字符集,其中_符号是固定格式,后面跟字符集名称,由于是可选项,因此一般都忽略了。
‘string‘:列或列值。
[collation_name]:指定校对规则。
下面SQL语句中选择的几个字符串的列值结果是相同的:
([email protected])[leedb]> select ‘cn 中国‘,_gbk‘cn 中国‘,_utf8‘cn 涓浗‘ collate utf8_general_ci;
+---------+---------+-----------------------------------------+
| cn 中国 | cn 中国 | _utf8‘cn 涓?浗‘ collate utf8_general_ci |
+---------+---------+-----------------------------------------+
| cn 中国 | cn 中国 | cn 中国 |
+---------+---------+-----------------------------------------+
1 row in set (0.00 sec)
这就是_charset_name表达式的作用,它会告诉解析器使用其后的字符串作为字符集。大多数情况下,我们都忽略了指定字符串的字符集,不管指定不指定,字符串都是有字符集的,那么MySQL是采用何种策略确定该字符串的字符集呢?这其中有一定的规则, 取决于当前的系统环境设置:
- 如果同时指定了字符集和校对规则,则按照指定的设置。
- 如果仅指定了校对规则,那么采用校对规则所属的字符集作为默认字符集。
- 如果均未指定(通常都是这样),那么会按照系统变量character_set_connection和 collation_connection的设置作为默认的字符集和校对规则。
2、错误提示的字符集
服务端返回的错误或警告信息也是字符,当然也有字符集.对于MySQL服务来说,服务端固定使用utl8字符集组织错误信息,返回到客户端时,转换成character_set_results系统变量指定的字符集。
服务端在组织错误或警告信息时,按照下列的方式处理信息的几个组成部分:
- 首先,消息模板使用utf8字符集。
- 而后,消息模板中的参数替换成指定的错误事件,包括几个子项:
> 标识符,比如表名或者列名这类内部即使用utf8字符集的仍然用该字符集输出。
> 字符串值(不含二进制)则从原始字符集转换成utf8。
> 二进制字符串转换成字节方式表示,范围在0x20Ox7e之间,超出该范围则使用\x十六进制编码。比如说一个键复制错误,尝试插入0x41CF9F到 VARBINARY唯一列时,返回的错误信息如下:
Duplicate entry ‘A\xC3\x9f‘ for key 1
这些信息被组合后返回消息到客户端,服务器将其从utf8转换成character_set_results系统变量所指定的字符集,如果 character_set_results变量值为空或binary,那么就不会发生转换操作,当然如果该变量设置的值就是UTF8也不会发生转换。
如果信息中的字符串,不能被以character_set_results变量中所指定的字符集展示,那么转换过程中可能就会触发另外的编码方案:
- 字符在基本多文种平面(Basic Multilingual Plane、BMP,也即Unicode编码的0号平面)范围0x0000OxFFFF区间的使用n\nnnnn标记输出。
- 字符不在BMP范围0x01000OxlOFFFF区间的使用n\+nnnnnnn标识输出。
设置错误信息的语言
默认情况下,mysqld进程会使用英文返回错误信息,这当然不代表它只能以英文显示,实现上它还支持其他很多种语言,包括 Czech、Danish、Dutch、Estonian、French、German、
Greek、Hungarian、Italian、Japanese、Korean、Norwegian、Norwegian-ny、Polish、Portuguese、 Romanian、Russian、Slovak、Spanish或者Swedish,用户可以选择上面提到的任意一种语言来显示错误信息,遗憾的是目前还不支持中文。
如何设置错误信息的显示语言呢?
MySQL服务在启动时,会读取--lc_messages_dir选项的值,并会在该选指定的路径下查找错误信息对应的语言文件。该选项的默认路径是MySQL软件安装目录下的share目录。例如:
[[email protected] ~]$ ll /usr/local/mysql/share/
总用量 1424
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 aclocal
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 bulgarian
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 charsets
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 czech
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 danish
-rw-r--r--. 1 mysql mysql 25575 7月 3 2013 dictionary.txt
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 dutch
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 english
-rw-r--r--. 1 mysql mysql 503623 7月 3 2013 errmsg-utf8.txt
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 estonian
-rw-r--r--. 1 mysql mysql 699282 7月 3 2013 fill_help_tables.sql
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 french
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 german
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 greek
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 hungarian
-rwxr-xr-x. 1 mysql mysql 3963 7月 3 2013 innodb_memcached_config.sql
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 italian
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 japanese
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 korean
-rw-r--r--. 1 mysql mysql 1695 7月 3 2013 mysql_security_commands.sql
-rw-r--r--. 1 mysql mysql 3506 7月 3 2013 mysql_system_tables_data.sql
-rw-r--r--. 1 mysql mysql 92882 7月 3 2013 mysql_system_tables.sql
-rw-r--r--. 1 mysql mysql 10375 7月 3 2013 mysql_test_data_timezone.sql
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 norwegian
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 norwegian-ny
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 polish
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 portuguese
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 romanian
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 russian
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 serbian
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 slovak
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 spanish
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 swedish
drwxr-xr-x. 2 mysql mysql 4096 5月 1 22:51 ukrainian
还有一个选项--lc_messages, 这个选项就是用来指定错误信息的语言。
lc_messages_dir是个只读的系统变量,MySQL服务启动后就无法修改,它只是指定路径而己,决定语言的lc_messages系统变量则可以在MySQL服务运行时随意修改,并且既可以在全局粒度修改,也可以在会话粒度修改。
修改当前错误信息的语言为法文,即修改当前会话中lc_messages系统变量的值,执行命令如下:
([email protected])[leedb]> set lc_messages=fr_FR;
Query OK, 0 rows affected (1.81 sec)
触发个错误看看:
([email protected])[leedb]> select abc;
ERROR 1054 (42S22): Champ ‘abc‘ inconnu dans field list
上面提到的两个系统变量lc_messages_dir和lc_messages是从MySQL 5.5 版本才开始引入的,在之前的版本中,要设置语言是通过--language选项,该参数相当于-lc_messages_dir和-lc_messages的集合,比如说,仍然设置法语显示,则在MySQL 5.5之前的版本中,需要在启动时加载-language选项,执行命令如下:
$ mysqld safe --language=/usr/local/mysql55/share/french
3、国家字符集
MySQL中还存在NCHAR、NVARCHAR这样的字符类型,这其中的N代表的就是NATIONAL,这类字符类型在MySQL数据库中拥有固定的字符集设置,在MySQL 5.6版本中,使用utf8作为这种类型在存储时的字符集。
也就是说,在MySQL 5.6版本中,不管如何设置字符集,当使用N[char/varchar/text] 数据类型时,这些列的字符集均为utf8.这也是它被称为“国家字符集”的原因,拥有更好的兼容性,不管所使用的字符究竟是何种,均能够被正确支持和保存。
基于这一点,下面几种列的字义在功能上是完全相同的:
CHAR(10) CHARACTER SET utf8
NATIONAL CHARACTER(10)
NCHAR(IO)