前言
在更新数据库时,有时会遇到这样的错误:
Illegal mix of collations (gbk_chinese_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '='
这是什么原因呢,先来讲一下字符集的相关知识吧。
深入分析问题
1、MySQL5的字符集支持
MySQL5.1 中的字符集支持包括在MyISAM、MEMORY和InnoDB存储引擎中。MySQL5支持多种字符集来存储字符串,对每种字符集也有相应的校对规则 (Collation)来进行比较。MySQL5还支持从服务器、数据库到数据库表、列、连接等多种级别的字符集和校对规则。
2、MySQL5字符集及校对规则
MySQL5字符集和校对规则有4个级别的默认设置:服务器级、数据库级、表级和连接级。
1)服务器字符集和校对
MySQL服务器有一个服务器字符集和一个服务器校对规则,它们均不能设置为空。
MySQL按照如下方法确定服务器字符集和服务器校对规则:
·当服务器启动时根据有效的选项设置
·根据运行时的设定值
可以通过在构建安装程序时将--with-charset和--with-collation作为configure的参数来设定服务器字符集和校对。
如 shell> ./configure --with-charset=latin1 --with-collation=latin1_german1_ci
运行时服务器字符集和校对规则是character_set_server和collation_server这两个系统变量表示的,可以通过修改它们来相应的改变。
2)数据库字符集和校对
每一个数据库有一个数据库字符集和一个数据库校对规则,它不能够为空。MySQL这样选择数据库字符集和数据库校对规则:
·如果在数据库创建SQL中指定了CHARACTER SET X和COLLATE Y,那么采用字符集X和校对规则Y。如 CREATE DATABASE db_name DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
·如果指定了CHARACTER SET X而没有指定COLLATE Y,那么采用CHARACTER SET X和CHARACTER SET X的默认校对规则(查看什么是默认校对规则)。
·否则,采用服务器字符集和服务器校对规则。即MYSQL系统变量character_set_server和collation_server的值
默认数据库的字符集和校对规则通过character_set_database和 collation_database系统变量的值表示。无论何时默认数据库更改了,服务器都设置这两个变量的值。如果没有 默认数据库,这两个变量与相应的服务器级别的变量(character_set_server和collation_server)具有相同的值。
3)表字符集和校对
每一个表有一个表字符集和一个校对规则,它不能为空。MySQL按照下面的方式选择表字符集和校对规则:
· 如果在创建表的SQL语句中指定了CHARACTER SET X和COLLATE Y,那么采用CHARACTER SET X和COLLATE Y。如 CREATE TABLE A DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
·如果指定了CHARACTER SET X而没有指定COLLATE Y,那么采用CHARACTER SET X和CHARACTER SET X的默认校对规则。
·否则,采用数据库字符集和数据库校对规则。
如果在列定义中没有指定列字符集和校对规则,则默认使用表字符集和校对规则。表字符集和校对规则是MySQL的扩展;在标准SQL中没有。
4)列字符集和校对
每一个“字符”列(即,CHAR、VARCHAR或TEXT类型的列)有一个列字符集和一个列校对规则,它不能为空。MySQL按照下面的方式选择列字符集和校对规则:
·如果指定了CHARACTER SET X和COLLATE Y,那么采用CHARACTER SET X和COLLATE Y。如
CREATE TABLE Table1
(
column1 VARCHAR(5) CHARACTER SET latin1 COLLATE latin1_german1_ci
);
·如果指定了CHARACTER SET X而没有指定COLLATE Y,那么采用CHARACTER SET X和CHARACTER SET X的默认校对规则。
·否则,采用表字符集和服务器校对规则。
5)连接字符集和校对
在客户端和服务器的连接处理中也涉及了字符集和校对规则变量。它们分别是character-set-client,character-set-connnection,character-set-results,collation-connection。
客户端发送SQL语句,例如查询,通过连接发送到服务器。服务器通过连接发送响应给客户端,例如结果集。
·客户端发送查询命令及请求数据,服务器要把这些数据当做什么字符集呢?
服务器使用character_set_client变量作为客户端发送的查询中使用的字符集。
·服务器接收到查询后应该转换为哪种字符集?
转换时,服务器使用character_set_connection和collation_connection系统变量。它将客户端发送的查询从 character_set_client指定的字符集转换为character_set_connection指定的字符集(除非字符串文字具有象_latin1或 _utf8的引介词)。collation_connection对比较文字字符串是重要的。对于列值的字符串比较,它不重要,因为列具有更高的校对规则优先级。
·服务器发送结果集或返回错误信息到客户端之前应该转换为哪种字符集?
character_set_results变量指示服务器返回查询结果到客户端使用的字符集。包括结果数据,例如列值和结果元数据(如列名)。
用户可以修改这几个系统变量的值。在MYSQL中,SET NAMES 'charset_name'能够影响连接字符集
SET NAMES 'x' 语句与下列三个语句等价:
mysql> SET character_set_client = x;mysql> SET character_set_results = x;mysql> SET character_set_connection = x;
7)字符串文字字符集和校对
每一字符串字符文字有一个字符集和一个校对规则,它不能为空。一个字符串文字可能有一个可选的字符集引用介词和COLLATE子句。如 SELECT _latin1'string' COLLATE latin1_danish_ci;
MySQL这样确定一个文字字符集和校对规则:
·如果指定了CHARACTER SET X和COLLATE Y,那么使用CHARACTER SET X和COLLATE Y。
·如果指定了CHARACTER SET X而没有指定COLLATE Y,那么使用CHARACTER SET X和CHARACTER SET X的默认校对规则。
·否则,使用通过character_set_connection和collation_connection系统变量给出的字符集和校对规则。
3、异常原因分析
通过上述对MySQL5字符集和校对规则的了解,就能够很清楚的分析出前言中的异常发生的原因了:
异常发生时的MySQL字符集和校对方式情况:
character_set_connection=utf8,校对方式为utf8默认校对方式
character_set_server=utf8,校对方式为utf8默认校对方式
character_set_database=utf8,校对方式为utf8默认校对方式
对于异常 Illegal mix of collations (gbk_chinese_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) for operation '=';
分析:其发生的SQL语句形如:SELECT COUNT(*) FROM urltable WHERE url='http://snap.xxx.com/中文';由于没有显示的指定字符串的字符集,因此MySQL采用 character_set_connection和collation_connection系统变量指定的值来处理,即utf8编码字符串,以 utf8的默认校对规则来处理'http://snap.xxx.com/中文';同时,由于列url是gbk编码的,因此MySQL对 url列采用的是gbk的字符集和gbk默认校对规则处理。因此在该SQL语句中'='两端的校对规则不一致,也就产生了先前的异常。
4、总结一下查询时的转换过程
1. MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;
2. 进行内部操作前将请求数据从character_set_connection转换为内部操作字符集,其确定方法如下:
• 使用每个数据字段的CHARACTER SET设定值;
• 若上述值不存在,则使用对应数据表的DEFAULT CHARACTER SET设定值(MySQL扩展,非SQL标准);
• 若上述值不存在,则使用对应数据库的DEFAULT CHARACTER SET设定值;
• 若上述值不存在,则使用character_set_server设定值。
3. 将操作结果从内部操作字符集转换为character_set_results。
相关资料
• 字符(Character)是指人类语言中最小的表义符号。例如’A'、’B'等;
• 给定一系列字符,对每个字符赋予一个数值,用数值来代表对应的字符,这一数值就是字符的编码(Encoding)。例如,我们给字符’A'赋予数值0,给字符’B'赋予数值1,则0就是字符’A'的编码;
• 给定一系列字符并赋予对应的编码后,所有这些字符和编码对组成的集合就是字符集(Character Set)。例如,给定字符列表为{‘A’,'B’}时,{‘A’=>0, ‘B’=>1}就是一个字符集;
• 校对规则(Collation)是指在同一字符集内字符之间的比较规则;
• 确定校对规则后,才能在一个字符集上定义什么是等价的字符,以及字符之间的大小关系;
• 每个校对规则唯一对应一种字符集,但一个字符集可以对应多种字符序,其中有一个是默认字符序(Default Collation);
• MySQL中的校对规则名称遵从命名惯例:以字符序对应的字符集名称开头;以_ci(表示大小写不敏感)、_cs(表示大小写敏感)或_bin(表示按编码值比较)结尾。例如:在字符序“utf8_general_ci”下,字符“a”和“A”是等价的;
– 格式为:[_charset] ‘string’ [COLLATE collation]
– 例如:
- SELECT _latin1 ‘string’;
- SELECT _utf8 ‘你好’ COLLATE utf8_general_ci;
– 由introducer修饰的文本字符串在请求过程中不经过多余的转码,直接转换为内部字符集处理。
参考:
http://www.laruence.com/2008/01/05/12.html