• MySQL提供了多种字符集和排序规则选择,其中字符集设置和数据存储以及客户端与MySQL实例的交互相关,排序规则和字符串的对比规则相关• 字符集的设置可以为MySQL实例、数据库、表、列四个级别• MySQL设置字符集支持在InnoDB, MyISAM, Memory三个存储引擎
1 字符集和排序规则简介
• 排序规则的命令通常是以对应的字符集的名字为开头,并以自己的特定属性结尾,比如排序规则utf8_general_ci和latin1_swedish_ci就分别是对应utf8和latin1字符集的排序规则• 当排序规则特指某种语言时,则中间的部分就为这种语言的名字,比如utf8_turkish_ci和utf8_hungarian_ci就代表UTF8字符集中的土耳其语和匈牙利语
• 默认情况下的字符集
• 在默认下创建的表默认字符集:
• ASCII码(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语• ASCII的局限在于只能显示26个基本拉丁字母、阿拉伯数目字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中的外来词如naïve、café、élite等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)• Latin字符集:ISO 8859-1,正式编号为ISO/IEC 8859-1:1998,又称Latin-1或“西欧语言”,是国际标准化组织内ISO/IEC 8859的第一个8位字符集。它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入96个字母及符号,藉以供使用附加符号的拉丁字母语言使用• 此字符集支持部分于欧洲使用的语言,包括阿尔巴尼亚语、巴斯克语、布列塔尼语、加泰罗尼亚语、丹麦语、荷兰语、法罗语、弗里西语、加利西亚语、德语、格陵兰语、冰岛语、爱尔兰盖尔语、意大利语、拉丁语、卢森堡语、挪威语、葡萄牙语、里托罗曼斯语、苏格兰盖尔语、西班牙语及瑞典语。
• Latin1是一种很常见的字符集,这种字符集是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。很明显,Latin1覆盖了所有的单字节,因此,可以将任意字符串保存在latin1字符集中,而不用担心有内容不符合latin1的编码规范而被抛弃。——gbk和utf8是多字节编码,没有这种特性。• mysql使用者经常利用Latin1的这种全覆盖特性,将其它类型的字符串,gbk,utf8,big5等,保存在latin1列中。保存的过程中没有数据丢失,只要原样取出来,便又是合法的gbk/utf8/big字符串。如果将gbk字符串保存在utf8列中,则gbk字符串中那些不符合utf8编码格式的内容,会被抛弃,保存的内容无法原样取出,数据实际上遭到了破坏。• 综上,如果我们看到一个字段的字符集是latin1的,那么,他保存的可能是任何编码的字符串• 所谓单字节编码就是挨着一个个来,比如圣诞节到了,你要送妹子一箱苹果,为制造浪漫,商铺提供两种包装方式,一是按个数来,即单个苹果包装进一个盒子,来一个包装一个,这样,妹子在拆完所有的盒子后完完整整的可以还原为一个个完整的和一箱完好无损的苹果,二是按重量来,每个盒子限重2两、3两、6两,这样在包装时,若刚好重3两的当然可以完整的放进一个盒子,但是若不够或者多了,勉不了要切开苹果,或者再往盒子中添加其他的部分苹果,这样的话,妹子再无论怎样拆开盒子,都会得到一箱残缺不堪的苹果了,因为你在按照这种包装方式进行时,已经破坏了单个苹果的完整性,现在还原不回来了~我们的字符集编码转换就是在做这种重新包装的工作,latin1恰好就像单个苹果包装,而utf8就像第二种方式。
• 实例级别的字符集可以在实例启动的时候指定,默认的字符集是latin1,可以通过--character-set-server参数指定实例的字符集,通过--collation-server参数指定实例的排序规则• 当仅指定了字符集没有指定排序规则时,则默认的排序规则是该字符集的默认排序规则• 以下三个启动实例的方式的设置效果是一样的:
• 指定实例级别的字符集的作用是当create database语句中没有指定字符集,则创建的数据库默认是该字符集和排序规则• 可以通过MySQL系统变量查看实例的字符集和排序规则,变量名分别是character_set_server和collation_server
3 表级别字符集和排序规则• 每个表都有自己的字符集和排序规则,可以通过create table和alter table语句指定或修改表的字符集和排序规则• CREATE TABLE t1 ( ... ) CHARACTER SET latin1 COLLATE latin1_danish_ci;• 当创建和修改表时没有指定排序规则时,会使用字符集默认的排序规则• 当创建和修改表时没有指定字符集,则使用排序规则对应的字符集• 当两个属性都没有指定时,会使用数据库级别的字符集和排序规则
4 列级别字符集和排序规则• 每个字符串字段(char, varchar, text)都有自己的字符集和排序规则,可以通过create table和alter table指定和修改字段的字符集和排序规则
• 同样,当字段上只指定了字符集时,排序规则会使用该字符集对应的默认排序规则• 当字段上只指定了排序规则时,则字符集会使用对应的字符集• 当字段上两者都没有定义时,则使用表级别的字符集和排序规则
5 字符串的字符集和排序规则• 对于在SQL语句中出现的字符串,也会有自己的字符集和排序规则,通常是由character_set_connection和collation_connection两个系统参数决定的• 也可以显示指定字符串的字符集和排序规则
• 当charset和collation都指定时,使用这两者作为字符串的字符集和排序规则• 当仅指定charset时,则排序规则使用该字符集对应的默认排序规则• 当仅指定排序规则时,则使用character_set_connection参数对应的字符集,且必须保证排序规则是字符集允许的• 当两者都没有指定时,则使用character_set_connection和collation_connection两个参数指定的字符集和排序规则• 比如SELECT _utf8'Müller';会使用utf8字符集和默认的utf8_general_ci排序规则• 比如SELECT 'Müller' COLLATE utf8_general_ci;会使用系统连接的字符集,但如果字符集不是utf8就会报错
6 国家字符集• 标准SQL中会有两种预定义的字符集数据类型,分别是nchar和nvarchar。 在MySQL中这种预定义的字符集就是UTF8,所以以下定义的最终结果是相同的:
7 连接级字符集和排序规则• 每个数据库客户端连接都有自己的字符集和排序规则属性, 客户端发送的语句的字符集是由character_set_client决定, 而与服务端交互时会根据character_set_connection和collation_connection两个参数将接收到的语句转化。 当涉及到显示字符串的比较时,由collation_connection参数决定,而当比较的是字段里的字符串时则根据字段本身的排序规则决定• character_set_result参数决定了语句的执行结果以什么字符集返回给客户端
8 应用程序配置字符集和排序规则• 当应用程序使用MySQL默认的字符集和排序规则时,则应用程序端不需要特别的设置,而当不是使用默认属性时则需要特别设置。 比如我们可以灵活的的为每一个数据库设置不同的字符集和排序规则,对应用程序来说就可以通过set names命令与所操作的数据库保持一致; 在服务器端我们可以通过设置--character_set_server和--collation_server两个参数来指定实例的默认字符集和排序规则,并将此作为每个数据库和表的默认字符集和排序规则,比如:
• 而在应用程序端,也需要通过set names命令保证每个数据库链接和数据库保持一致
• 我们也可以通过参数 --init_connect="SET NAMES 'utf8'" 来让每个客户端连接都自动设置字符集,但缺点是对拥有super权限的用户不生效• init_connect表示服务器为每个连接的客户端执行的字符串。字符串由一个或多个SQL语句组成。要想指定多个语句,用分号间隔开
• 我们也可以通过参数 --init_connect="SET NAMES 'utf8'" 来让每个客户端连接都自动设置字符集,但缺点是对拥有super权限的用户不生效• init_connect表示服务器为每个连接的客户端执行的字符串。字符串由一个或多个SQL语句组成。要想指定多个语句,用分号间隔开
9 排序规则在SQL中的使用
首先计算每种情况的排序规则权重:1. 当有显示的写明优先级的,则权重为02. 当两个有不同排序规则的字符串连接在一起的,则权重为13. 字段和本地参数的排序规则,权重为24. 由部分字符串函数返回的系统常量所带的排序规则,权重为35. 字符串自带的排序规则,权重为4......在最终选择使用哪种排序规则上,规则为:1. 优先使用权重最低的2. 如果两者拥有相同的优先级,则 1)如果两者都是Unicode,或者都不是Unicode,则返回错误 2)如果一边是Unicode而另一边不是,则将不是Unicode的一边转化成Unicode字符集, 比如:SELECT CONCAT(utf8_column, latin1_column) FROM t1; latin1字符集的列会自动转化为utf8的字符集,再执行字符串连接 3)当两边都是相同字符集,且一边是_bin而另一边是_ci/_cs的排序规则时,则使用_bin的排序规则
我们不能比较两个属于不同校对的不同字符值
10 Unicode字符集Unicode(Universal Code)是一种在计算机上使用的字符编码。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode存在不同的编码方案,包括Utf-8,Utf-16和Utf-32。Utf表示Unicode Transformation Format。
• MySQL支持的Unicode字符集包括以下几种:
大多数的Unicode字符集都会一个一般意义上的排序规则,比如以_general结尾命名,以_bin命名的二进制排序规则,当然也有指定特定语言的排序规则• 比如在“Classical Latin”的排序规则中, I和J的对比结果是相等的, 而U和V的对比结果也是相等的
11 不同字符集下字符空间消耗
其中M在非二进制字段中代表定义的字符长度数值,L代表真实的字符所占用的字节数当定义varchar字段时,最大的长度为65553个字节,且这个最大长度是整行数据共享的限制,。当存储比如Latin字符集时,每个字节存储一个字符,而当在多字节字符集比如UTF8时,由于每个字符最大要求3个字节的存储,所以字段的最大创建长度就是21844个字符
12 MySQL字符集参数查看当前数据库字符集
查看当前数据库字符集校对
在启动参数前指定
临时指定
又比如向默认字符集为latin1的数据表插入utf8编码的数据前设置了连接字符集为utf8插入时根据连接字符集设置,character_set_client、character_set_connection和character_set_results均为utf8;插入数据将经过utf8=>utf8=>latin1的字符集转换,若原始数据中含有\u0000~\u00ff范围以外的Unicode字符,会因为无法在latin1字符集中表示而被转换为“?”(0×3F)符号,以后查询时不管连接字符集设置如何都无法恢复其内容了
15 乱码终极解决方案1.首先要明确你的客户端时候何种编码格式,这是最重要的(IE6一般用utf8,命令行一般是gbk,一般程序是gb2312)2.确保你的数据库使用utf8格式,很简单,所有编码通吃。3.一定要保证connection字符集大于等于client字符集,不然就会信息丢失,比如: latin1 < gb2312 < gbk < utf8,若设置set character_set_client = gb2312,那么至少connection的字符集要大于等于gb2312,否则就会丢失信息4.以上三步做正确的话,那么所有中文都被正确地转换成utf8格式存储进了数据库,为了适应不同的浏览器,不同的客户端,你可以修改character_set_results来以不同的编码显示中文字体,由于utf8是大方向,因此web应用还是倾向于使用utf8格式显示中文的。