MySQ—字符集和比较规则

小龙 548 2022-02-10

字符集简介

在计算机中只能存储二进制数据;想要存储字符串,则需要建立字符串与二进制数据的映射关系,建立这个关系首先要搞清楚两件事:

  1. 你要把那些字符串映射成二进制数据?(界定清楚字符范围)
  2. 怎么映射?(将一个字符串映射成二进制数据的过程也叫作编码,将二进制数据映射成字符串的过程叫做解码

人们抽象出一个字符集的概念来描述某个字符范围的编码规则。

比方说我们来自定义一个名称为xiaolong的字符集,它包含的字符范围和编码规则如下:

包含字符’a’、‘b’、‘A’、‘B’。

编码规则如下:

采用1个字节编码一个字符的形式,字符和字节的映射关系如下:

'a' -> 00000001 (十六进制:0x01)
'b' -> 00000010 (十六进制:0x02)
'A' -> 00000011 (十六进制:0x03)
'B' -> 00000100 (十六进制:0x04)

有了xiaolong字符集,我们就可以用二进制形式表示一些字符串了,下边是一些字符串用xiaolong字符集编码后的二进制表示:

'bA' -> 0000001000000011  (十六进制:0x0203)
'baB' -> 000000100000000100000100  (十六进制:0x020104)
'cd' -> 无法表示,字符集xiaolong不包含字符'c'和'd'

比较规则简介

当我们确定了"xiaolong"字符集表示表示的字符的范围及编码规则之后,我们可以很轻易的联想到直接通过字符的二进制编码来比较大小;比如:字符"a"的编码为0x01,字符"b"编码为0x02,所以"a"小于"b",这种简单的比较规则被称为二进制比较规(binary collation)

这种比较规则很简单,但有时候不符合现实需求,比如在很多场景中英文字母是不区分大小写的;在比如我们的汉字,汉字有几万之多。对于某一种字符集来说,比较两个字符大小的规则可以制定很多种,也就是同一种字符集可以有很多种比较规则

一些重要的字符集

这个世界很大,语言有很多种,所以由不同的人定义出了非常的字符集,这些不同字符集表示的字符范围和用到的比较规则可能都不一样。我只举一些最常见的字符集

  • ASCII字符集

共收录128个字符,包括空格、标点符号、数字、大小写字母和一些不可见字符,由于一共才128个字符,所以使用一个字节来进行编码(一个字节 8个byte位 00000000)

'L' ->  01001100(十六进制:0x4C,十进制:76)
'M' ->  01001101(十六进制:0x4D,十进制:77)
  • ISO 8859-1字符集

共收录258个字符,是在ASCII字符集的基础上进行扩充了128个西欧常用的字符,也使用一个字节来进行编码。它还有个别名:latin1

  • GB2312字符集

收录了汉字及拉丁字母、希腊字母、日文平假名及片假字母、俄语西里尔字母。其中收录汉字6763个,其他文字符号682,共7445字符。同时这种字符集又兼容ACSII字符集,所以在编码上的方式有些奇怪:
|- 如果该字符在ASCII字符集中,则采用1字节编码
|- 不存在ASCII字符集上则采用2字节编码

这种一个字符需要的字节数可能不同的编码方式被称为变长编码方式。

  • GBK字符集

GBK字符集只是在收录字符的范围上对GB2312字符集进行了扩充,编码方式上向上兼容GB2312

  • UTF8字符集

收录了地球上能想到的所有的字符,而且在不断的扩充。这种字符集兼容ASCII,所以采用变长编码方式,编码一个字符需要1~4个字节

'L' ->  01001100(十六进制:0x4C)
'啊' ->  111001011001010110001010(十六进制:0xE5958A)
小贴士:

其实准确的说,utf8只是Unicode字符集的一种编码方案,Unicode字符集可以采用utf8、utf16、utf32这几种编码方案,utf8使用1~4个字节编码一个字符,utf16使用2个或4个字节编码一个字符,utf32使用4个字节编码一个字符。更详细的Unicode和其编码方案的知识不是本书的重点,大家上网查查哈~

MySQL中并不区分字符集和编码方案的概念,所以后边唠叨的时候把utf8、utf16、utf32都当作一种字符集对待。

对于同一个字符,在不同的字符集上采用的编码方式是完全不同的,比如说“我”字

utf8编码:111001101000100010010001 (3个字节,十六进制表示是:0xE68891)
gb2312编码:1011000010101110 (2个字节,十六进制表示是:0xB0AE)

MySQL中支持的字符集和比较规则

我们指定utf8字符集表示一个字符需要1 ~ 4个字节,但是我们常用的一些字符通常使用1~3个字节就可以表示。在MySQL中一个字符所用最大字节长度在某些方面会影响系统的存储和性能,所以MySQL的设计者对uft8字符集定义两个概念:

  1. utf8mb3:阉割过的utf8字符集,只是用1~3个字节表示字符
  2. utf8mb4:正宗的utf8字符集,使用1~4个字节表示字符

注意:在MySQL中 utf8 就等于 utf8mb3

字符集查看

SHOW CHARSET;
+----------+---------------------------------+---------------------+--------+
| Charset  | Description                     | Default collation   | Maxlen |
+----------+---------------------------------+---------------------+--------+
| armscii8 | ARMSCII-8 Armenian              | armscii8_general_ci |      1 |
| ascii    | US ASCII                        | ascii_general_ci    |      1 |
| big5     | Big5 Traditional Chinese        | big5_chinese_ci     |      2 |
| binary   | Binary pseudo charset           | binary              |      1 |
... ...
| gb18030  | China National Standard GB18030 | gb18030_chinese_ci  |      4 |
| gb2312   | GB2312 Simplified Chinese       | gb2312_chinese_ci   |      2 |
| gbk      | GBK Simplified Chinese          | gbk_chinese_ci      |      2 |
... ...
| utf16    | UTF-16 Unicode                  | utf16_general_ci    |      4 |
| utf16le  | UTF-16LE Unicode                | utf16le_general_ci  |      4 |
| utf32    | UTF-32 Unicode                  | utf32_general_ci    |      4 |
| utf8     | UTF-8 Unicode                   | utf8_general_ci     |      3 |
| utf8mb4  | UTF-8 Unicode                   | utf8mb4_0900_ai_ci  |      4 |
+----------+---------------------------------+---------------------+--------+
41 rows in set (0.00 sec)

在MySQL8的版本里一共支持41中字符集,Charset:表示字符集名称、Default collation:表示默认支持的比较规则、Maxlen:表示最大占用字节数

比较规则查看

SHOW COLLATION LIKE 'utf8mb4%';
+----------------------------+---------+-----+---------+----------+---------+---------------+
| Collation                  | Charset | Id  | Default | Compiled | Sortlen | Pad_attribute |
+----------------------------+---------+-----+---------+----------+---------+---------------+
| utf8mb4_0900_ai_ci         | utf8mb4 | 255 | Yes     | Yes      |       0 | NO PAD        |
| utf8mb4_0900_as_ci         | utf8mb4 | 305 |         | Yes      |       0 | NO PAD        |
| utf8mb4_0900_as_cs         | utf8mb4 | 278 |         | Yes      |       0 | NO PAD        |
| utf8mb4_0900_bin           | utf8mb4 | 309 |         | Yes      |       1 | NO PAD        |
... ... 
| utf8mb4_vi_0900_as_cs      | utf8mb4 | 300 |         | Yes      |       0 | NO PAD        |
| utf8mb4_zh_0900_as_cs      | utf8mb4 | 308 |         | Yes      |       0 | NO PAD        |
+----------------------------+---------+-----+---------+----------+---------+---------------+
75 rows in set (0.00 sec)

可以看出utf8mb4字符集一共有75中比较规则

观察这些比较规则可以发现一个规律,规则的结尾都有:_ai、_ci、_bin;这些不同的后缀意味着比较规则中是否区分重要音、大小写的啥的。

后缀 英文释义 描述
_ai accent insensitive 不区分重音
_as accent sensitive 区分重音
_ci case insensitive 不区分大小写
_cs case sensitive 区分大小写
_bin binary 以二进制方式比较

比如: utf8mb4_0900_ai_ci这个比较规则表示:不区分重音及不区分大小写

字符集和比较规则的应用

各级别的字符集和比较规则

MySQL有4个级别的字符集和比较规则,分别是:

  • 服务器级别
  • 数据库级别
  • 表级别
  • 列级别

服务器级别

MySQL提供了两个系统变量来表示服务器级别的字符集和比较规则:

系统变量 描述
character_set_server 服务器级别的字符集
collation_server 服务器级别的比较规则

看一下这两个系统变量的值:

SHOW VARIABLES LIKE 'character_set_server';
+----------------------+---------+
| Variable_name        | Value   |
+----------------------+---------+
| character_set_server | utf8mb4 |
+----------------------+---------+
1 row in set (0.01 sec)

SHOW VARIABLES LIKE 'collation_server';
+------------------+--------------------+
| Variable_name    | Value              |
+------------------+--------------------+
| collation_server | utf8mb4_0900_ai_ci |
+------------------+--------------------+
1 row in set (0.00 sec)

可以发现在MySQL8中服务器级别默认的字符集是 utf8mb4,默认的比较规则是 utf8mb4_0900_ai_ci

我们可以在启动服务器程序时通过启动选项或者在服务器程序运行过程中使用SET语句修改这两个变量的值。比如我们可以在配置文件中这样写:

[server]
character_set_server=utf8mb4
collation_server=utf8mb4_0900_ai_ci

当服务器启动的时候读取这个配置文件后这两个系统变量的值便修改了。

数据库级别

我们在创建和修改数据库的时候可以指定该数据库的字符集和比较规则,具体语法如下:

CREATE DATABASE 数据库名
    [[DEFAULT] CHARACTER SET 字符集名称]
    [[DEFAULT] COLLATE 比较规则名称];

ALTER DATABASE 数据库名
    [[DEFAULT] CHARACTER SET 字符集名称]
    [[DEFAULT] COLLATE 比较规则名称]

其中的DEFAULT可以省略,并不影响语句的语义。比方说我们新创建一个名叫charset_demo_db的数据库,在创建的时候指定它使用的字符集为gb2312,比较规则为gb2312_chinese_ci:

CREATE DATABASE charset_demo_db
    -> CHARACTER SET gb2312
    -> COLLATE gb2312_chinese_ci;
Query OK, 1 row affected (0.01 sec)

如果想查看当前数据库使用的字符集和比较规则,可以查看下面两个系统变量的值(前提是使用USE语句选择当前默认数据库,如果没有默认数据库,则变量与相应的服务器级系统变量具有相同的值):

系统变量 描述
character_set_database 当前数据库的字符集
collation_database 当前数据库的比较规则
我们来查看一下刚刚创建的charset_demo_db数据库的字符集和比较规则:
# 切换到创建的数据库
use charset_demo_db;

SHOW VARIABLES LIKE 'character_set_database';
+------------------------+--------+
| Variable_name          | Value  |
+------------------------+--------+
| character_set_database | gb2312 |
+------------------------+--------+
1 row in set (0.00 sec)

SHOW VARIABLES LIKE 'collation_database';
+--------------------+-------------------+
| Variable_name      | Value             |
+--------------------+-------------------+
| collation_database | gb2312_chinese_ci |
+--------------------+-------------------+
1 row in set (0.00 sec)

可以看到这个charset_demo_db数据库的字符集和比较规则就是我们在创建语句中指定的。需要注意的一点是: character_set_database 和 collation_database 这两个系统变量是只读的,我们不能通过修改这两个变量的值而改变当前数据库的字符集和比较规则。

数据库的创建语句中也可以不指定字符集和比较规则,比如这样:

CREATE DATABASE 数据库名;

这样的话将使用服务器级别的字符集和比较规则作为数据库的字符集和比较规则。

表级别

我们也可以在创建和修改表的时候指定表的字符集和比较规则,语法如下:

CREATE TABLE 表名 (列的信息)
    [[DEFAULT] CHARACTER SET 字符集名称]
    [COLLATE 比较规则名称]]

ALTER TABLE 表名
    [[DEFAULT] CHARACTER SET 字符集名称]
    [COLLATE 比较规则名称]

比方说我们在刚刚创建的charset_demo_db数据库中创建一个名为t的表,并指定这个表的字符集和比较规则:

 CREATE TABLE t(
    ->    col VARCHAR(10)
    ->     ) CHARACTER SET utf8 COLLATE utf8_general_ci;
Query OK, 0 rows affected, 2 warnings (0.04 sec)

如果创建和修改表的语句中没有指明字符集和比较规则,将使用该表所在数据库的字符集和比较规则作为该表的字符集和比较规则。

列级别

需要注意的是,对于存储字符串的列,同一个表中的不同的列也可以有不同的字符集和比较规则。我们在创建和修改列定义的时候可以指定该列的字符集和比较规则,语法如下:

CREATE TABLE 表名(
    列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称],
    其他列...
);

ALTER TABLE 表名 MODIFY 列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称];

比如我们修改一下表t中列col的字符集和比较规则可以这么写:

ALTER TABLE t MODIFY col VARCHAR(10) CHARACTER SET gbk COLLATE gbk_chinese_ci;
Query OK, 0 rows affected (0.10 sec)
Records: 0  Duplicates: 0  Warnings: 0

对于某个列来说,如果在创建和修改的语句中没有指明字符集和比较规则,将使用该列所在表的字符集和比较规则作为该列的字符集和比较规则

仅修改字符集或仅修改比较规则

由于字符集和比较规则是互相有联系的,如果我们只修改了字符集,比较规则也会跟着变化,如果只修改了比较规则,字符集也会跟着变化,具体规则如下:

  • 只修改字符集,则比较规则将变为修改后的字符集默认的比较规则。
    -只修改比较规则,则字符集将变为修改后的比较规则对应的字符集。
    不论哪个级别的字符集和比较规则,这两条规则都适用,我们以服务器级别的字符集和比较规则为例来看一下详细过程:

  • 只修改字符集,则比较规则将变为修改后的字符集默认的比较规则。

SET character_set_server = gb2312;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW VARIABLES LIKE 'character_set_server';
+----------------------+--------+
| Variable_name        | Value  |
+----------------------+--------+
| character_set_server | gb2312 |
+----------------------+--------+
1 row in set (0.00 sec)

mysql>  SHOW VARIABLES LIKE 'collation_server';
+------------------+-------------------+
| Variable_name    | Value             |
+------------------+-------------------+
| collation_server | gb2312_chinese_ci |
+------------------+-------------------+
1 row in set (0.00 sec)

我们只修改了character_set_server的值为gb2312,collation_server的值自动变为了gb2312_chinese_ci。

  • 只修改比较规则,则字符集将变为修改后的比较规则对应的字符集。
SET collation_server = utf8_general_ci;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW VARIABLES LIKE 'character_set_server';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| character_set_server | utf8  |
+----------------------+-------+
1 row in set (0.00 sec)

mysql> SHOW VARIABLES LIKE 'collation_server';
+------------------+-----------------+
| Variable_name    | Value           |
+------------------+-----------------+
| collation_server | utf8_general_ci |
+------------------+-----------------+
1 row in set (0.00 sec)

我们只修改了collation_server的值为utf8_general_ci,character_set_server的值自动变为了utf8。

各级别字符集和比较规则小结

我们介绍的这4个级别字符集和比较规则的联系如下:

  • 如果创建或修改列时没有显式的指定字符集和比较规则,则该列默认用表的字符集和比较规则
  • 如果创建表时没有显式的指定字符集和比较规则,则该表默认用数据库的字符集和比较规则
  • 如果创建数据库时没有显式的指定字符集和比较规则,则该数据库默认用服务器的字符集和比较规则

客户端和服务器通信中的字符集

编码和解码使用的字符集不一致的后果

说到底,字符串在计算机上的体现就是一个字节串,如果你使用不同字符集去解码这个字节串,最后得到的结果可能让你挠头。

我们知道字符’我’在utf8字符集编码下的字节串长这样:0xE68891,如果一个程序把这个字节串发送到另一个程序里,另一个程序用不同的字符集去解码这个字节串,假设使用的是gbk字符集来解释这串字节,解码过程就是这样的:

  1. 首先看第一个字节0xE6,它的值大于0x7F(十进制:127),说明是两字节编码,继续读一字节后是0xE688,然后从gbk编码表中查找字节为0xE688对应的字符,发现是字符’鎴’
  2. 继续读一个字节0x91,它的值也大于0x7F,再往后读一个字节发现木有了,所以这是半个字符。
  3. 所以0xE68891被gbk字符集解释成一个字符’鎴’和半个字符。

假设用iso-8859-1,也就是latin1字符集去解释这串字节,解码过程如下:

  1. 先读第一个字节0xE6,它对应的latin1字符为æ。
  2. 再读第二个字节0x88,它对应的latin1字符为ˆ。
  3. 再读第三个字节0x91,它对应的latin1字符为‘。
  4. 所以整串字节0xE68891被latin1字符集解释后的字符串就是’我’

可见,如果对于同一个字符串编码和解码使用的字符集不一样,会产生意想不到的结果,作为人类的我们看上去就像是产生了乱码一样。

MySQL中字符集的转换

我们知道从客户端发往服务器的请求本质上就是一个字符串,服务器向客户端返回的结果本质上也是一个字符串,而字符串其实是使用某种字符集编码的二进制数据。这个字符串可不是使用一种字符集的编码方式一条道走到黑的,从发送请求到返回结果这个过程中伴随着多次字符集的转换,在这个过程中会用到3个系统变量,我们先把它们写出来看一下:

系统变量 描述
character_set_client 服务器解码请求时使用的字符集
character_set_connection 服务器处理请求时会把请求字符串从character_set_client转为character_set_connection
character_set_results 服务器向客户端返回数据时使用的字符集

这几个系统变量在我的计算机上的默认值如下(不同操作系统的默认值可能不同):

SHOW VARIABLES LIKE 'character_set_client';
+----------------------+---------+
| Variable_name        | Value   |
+----------------------+---------+
| character_set_client | utf8mb4 |
+----------------------+---------+
1 row in set (0.01 sec)

SHOW VARIABLES LIKE 'character_set_connection';
+--------------------------+---------+
| Variable_name            | Value   |
+--------------------------+---------+
| character_set_connection | utf8mb4 |
+--------------------------+---------+
1 row in set (0.00 sec)

SHOW VARIABLES LIKE 'character_set_results';
+-----------------------+---------+
| Variable_name         | Value   |
+-----------------------+---------+
| character_set_results | utf8mb4 |
+-----------------------+---------+
1 row in set (0.00 sec)

可以发现这几个系统变量的值都是utf8

为了体现出字符集在请求处理过程中的变化,我们这里特意修改一个系统变量的值:

set character_set_connection = gbk;
Query OK, 0 rows affected (0.00 sec)

SHOW VARIABLES LIKE 'character_set_connection';
+--------------------------+---------+
| Variable_name            | Value   |
+--------------------------+---------+
| character_set_connection |  gbk    |
+--------------------------+---------+
1 row in set (0.00 sec)

现在系统变量character_set_client和character_set_results的值还是utf8,而character_set_connection的值为gbk。现在假设我们客户端发送的请求是下边这个字符串:

SELECT * FROM t WHERE s = '我';

分析字符’我’在这个过程中字符集的转换过程

  1. 客户端发送请求所使用的字符集
    一般情况下客户端所使用的字符集和当前操作系统一致,不同操作系统使用的字符集可能不一样,如下:
    • 类Unix系统使用的是utf8
    • Windows使用的是gbk
      例如我在使用的centos操作系统时,客户端使用的就是utf8字符集。所以字符’我’在发送给服务器的请求中的字节形式就是:0xE68891
  2. 服务器接收到客户端发送来的请求其实是一串二进制的字节,它会认为这串字节采用的字符集是character_set_client,然后把这串字节转换为character_set_connection字符集编码的字符。

由于我的计算机上character_set_client的值是utf8,首先会按照utf8字符集对字节串0xE68891进行解码,得到的字符串就是’我’,然后按照character_set_connection代表的字符集,也就是gbk进行编码,得到的结果就是字节串0xCED2。

  1. 因为表t的列col采用的是gbk字符集,与character_set_connection一致,所以直接到列中找字节值为0xCED2的记录,最后找到了一条记录。
  2. 上一步骤找到的记录中的col列其实是一个字节串0xCED2,col列是采用gbk进行编码的,所以首先会将这个字节串使用gbk进行解码,得到字符串’我’,然后再把这个字符串使用character_set_results代表的字符集,也就是utf8进行编码,得到了新的字节串:0xE68891,然后发送给客户端。
  3. 由于客户端是用的字符集是utf8,所以可以顺利的将0xE68891解释成字符我,从而显示到我们的显示器上,所以我们人类也读懂了返回的结果。

image.png

从这个分析中我们可以得出这么几点需要注意的地方:

  • 服务器认为客户端发送过来的请求是用character_set_client编码的。

假设你的客户端采用的字符集和 character_set_client 不一样的话,这就会出现意想不到的情况。比如我的客户端使用的是utf8字符集,如果把系统变量character_set_client的值设置为ascii的话,服务器可能无法理解我们发送的请求,更别谈处理这个请求了。

  • 服务器将把得到的结果集使用character_set_results编码后发送给客户端。

假设你的客户端采用的字符集和 character_set_results 不一样的话,这就可能会出现客户端无法解码结果集的情况,结果就是在你的屏幕上出现乱码。比如我的客户端使用的是utf8字符集,如果把系统变量character_set_results的值设置为ascii的话,可能会产生乱码。

  • character_set_connection只是服务器在将请求的字节串从character_set_client转换为character_set_connection时使用,它是什么其实没多重要,但是一定要注意,该字符集包含的字符范围一定涵盖请求中的字符,要不然会导致有的字符无法使用character_set_connection代表的字符集进行编码。比如你把character_set_client设置为utf8,把character_set_connection设置成ascii,那么此时你如果从客户端发送一个汉字到服务器,那么服务器无法使用ascii字符集来编码这个汉字,就会向用户发出一个警告。

知道了在MySQL中从发送请求到返回结果过程里发生的各种字符集转换,但是为啥要转来转去的呢?不晕么?

答:是的,很头晕,所以我们通常都把 character_set_client 、character_set_connection、character_set_results 这三个系统变量设置成和客户端使用的字符集一致的情况,这样减少了很多无谓的字符集转换。为了方便我们设置,MySQL提供了一条非常简便的语句:

SET NAMES 字符集名;

这一条语句产生的效果和我们执行这3条的效果是一样的:

SET character_set_client = 字符集名;
SET character_set_connection = 字符集名;
SET character_set_results = 字符集名;

另外,如果你想在启动客户端的时候就把character_set_client、character_set_connection、character_set_results这三个系统变量的值设置成一样的,那我们可以在启动客户端的时候指定一个叫default-character-set的启动选项,比如在配置文件里可以这么写:

[client]
default-character-set=utf8

它起到的效果和执行一遍SET NAMES utf8是一样一样的,都会将那三个系统变量的值设置成utf8。

比较规则的应用

结束了字符集的漫游,我们把视角再次聚焦到比较规则,比较规则的作用通常体现比较字符串大小的表达式以及对某个字符串列进行排序中,所以有时候也称为排序规则。比方说表t的列col使用的字符集是gbk,使用的比较规则是gbk_chinese_ci,我们向里边插入几条记录:

 INSERT INTO t(col) VALUES('a'), ('b'), ('A'), ('B');

我们查询的时候按照t列排序一下:

SELECT * FROM t ORDER BY col;
+------+
| col  |
+------+
| a    |
| A    |
| b    |
| B    |
| 我   |
+------+
5 rows in set (0.00 sec)

可以看到在默认的比较规则gbk_chinese_ci中是不区分大小写的,我们现在把列col的比较规则修改为gbk_bin:

ALTER TABLE t MODIFY col VARCHAR(10) COLLATE gbk_bin;

再次观察

SELECT * FROM t ORDER BY col;
+------+
| col  |
+------+
| A    |
| B    |
| a    |
| b    |
| 我   |
+------+
5 rows in set (0.00 sec)

所以如果以后大家在对字符串做比较或者对某个字符串列做排序操作时没有得到想象中的结果,需要思考一下是不是比较规则的问题~

总结

  1. 字符集指的是某个字符范围的编码规则。
  2. 比较规则是针对某个字符集中字符比较大小的一种规则。
  3. MySQL中,一个字符集可以有若干中比较规则,其中有一个默认的,一个比较规则必定对应着一种字符集
  4. MySQL中查看支持的字符集和字符集对应的比较规则语法:
SHOW [CHARACTER|CHARSET] [LIKE 匹配模式]
SHOW COLLATION [LIKE 匹配模式]
  1. MySQL有四个级别的字符集和比较规则
    • 服务器级别

    character_set_server:表示服务器级别的字符集,collation_server:表示服务器级别的比较规则

    • 数据库级别

    创建和修改数据库时可以指定字符集和比较规则

    CREATE DATABASE 数据库名称
    	[[DEFAULT] CHARACTER SET 字符集名称]
    	[[DEFAULT] COLLATE 比较规则名称];
    ALTER DATABASE 数据库名称
    	[[DEFAULT] CHARACTER SET 字符集名称]
    	[[DEFAULT] COLLATE 比较规则名称];
    

    character_set_database:表示当前数据库默认的字符集,collation_database:表示当前数据库默认的比较规则,这两个系统变量只读,无法修改。如果在创建数据库时没有指定字符集和比较规则,则使用服务器默认的

    • 表级别

    创建和修改表时指定的字符集和比较规则

    CREATE TABLE 表名称
    	[[DEFAULT] CHARACTER SET 字符集]
    	[COLLATE 比较规则];
    ALTER TABLE 表名称
    	[[DEFAULT] CHARACTER SET 字符集]
    	[COLLATE 比较规则];
    
    • 列级别

    创建和修改列定义的时候可以指定该列的字符集和比较规则

    CREATE TABLE 表名(
    	列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则],
    	... ... 
    );
    ALTER TABLE 表名 MODIFY 列名 字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称];
    
  2. 从发送请求到接收结果过程中发生的字符集转换:
    • 客户端使用操作系统的字符集编码请求字符串,向服务器发送的是经过编码的一个字节串。
    • 服务器将客户端发送来的字节串采用character_set_client代表的字符集进行解码,将解码后的字符串再按照character_set_connection代表的字符集进行编码。
    • 如果character_set_connection代表的字符集和具体操作的列使用的字符集一致,则直接进行相应操作,否则的话需要将请求中的字符串从character_set_connection代表的字符集转换为具体操作的列使用的字符集之后再进行操作。
    • 将从某个列获取到的字节串从该列使用的字符集转换为character_set_results代表的字符集后发送到客户端。
    • 客户端使用操作系统的字符集解析收到的结果集字节串。

在这个过程中各个系统变量的含义如下

系统变量 描述
character_set_client 服务器解码请求时使用的字符集
character_set_connection 服务器处理请求时会把请求字符串从character_set_client转为character_set_connection
character_set_results 服务器向客户端返回数据时使用的字符集

一般情况下要使用保持这三个变量的值和客户端使用的字符集相同。

  1. 比较规则的作用通常体现比较字符串大小的表达式以及对某个字符串列进行排序中。

# mysql