MySQL 学习笔记 - 字符集问题

字符集

1. 常规字符集和校对

字符集 是一套符号和编码。校对规则 是在字符集内用于比较字符的一套规则。

# 列出可以用的字符集
mysql> Show Character Set;

任何一个给定的字符集至少有一个校对规则。它可能有几个校对规则。

# 列出给定字符集的校对规则
mysql> SHOW COLLATION LIKE 'latin1%';

2. 默认字符集

  • 服务器字符集和校对。

当服务器启动时根据有效的选项设置,根据运行时的设定值

  • 数据库字符集和校对

    CREATE DATABASE db_name [[DEFAULT] CHARACTER SET charset_name] [[DEFAULT]  COLLATE collation_name]
  • 表字符集和校对

    CREATE TABLE tbl_name (column_list) [DEFAULT CHARACTER SET charset_name [COLLATE collation_name]]
  • 列字符集和校对

    col_name {CHAR | VARCHAR | TEXT} (col_length) [CHARACTER SET charset_name [COLLATE collation_name]]

使用 \s 命令可以查看当前数据库是设置情况。

3. 连接字符集和校对

每一个客户端有一个连接相关的字符集和校对规则变量。

  • 服务器接收到查询后应该转换为哪种字符集?

    例如:
    语句 insert into <Table> values (“测试”) 执行时,服务器收到数据,会将客户端发送的查询从character_set_client 系统变量转换到 character_set_connection。然后数据库存放数据时,再会从character_set_connection 转成当前数据实际上设置的字符集。

  • 服务器发送结果集或返回错误信息到客户端之前应该转换为哪种字符集?

    返回结果从数据库实际上的字符集转换成 character_set_results 指示的字符集传给客户端。包括结果数据,例如列值和结果元数据(如列名)。

有两个语句影响连接字符集:

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;

当一个客户端连接时,它向服务器发送希望使用的字符集名称。服务器为那个字符集设置character_set_client、character_set_results和 character_set_connection变量。(实际上,服务器为使用该字符集执行一个SET NAMES操作。)

4. 字符串文字字符集和校对

例如:

SELECT 'string';
SELECT _latin1'string';
SELECT _latin1'string' COLLATE latin1_danish_ci;

对于简单的语句SELECT 'string',字符串使用由character_set_connectioncollation_connection系统变量定义的字符集和 校对规则。

参考:

1. 先来看看latin1 (参考百度百科)

  

Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。ISO-8859-1收录的字符除ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。欧元符号出现的比较晚,没有被收录在ISO-8859-1当中。因为ISO-8859-1编码范围使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。换言之,把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。这是个很重要的特性,MySQL数据库默认编码是Latin1就是利用了这个特性。ASCII编码是一个7位的容器,ISO-8859-1编码是一个8位的容器。

2. 稍微再想想字符集

是的,不用纠结太多了,如果数据库内表的字符集是latin1,那么默认情况下中文也可被支持!

  1. latin1覆盖了所有单字节的值,任何其他的码流都可以被看做latin1
  2. 把一个gbk编码的串写入latin1的表,不会有任何问题,保存的是原封不动的字节流
  3. 从表中读取已写入的串也不会有任何问题,且读出的字节流就和当初写入的完全一致

在缺省字符集为 latin1的mysql中,我们通常就把GB字符集的汉字保存到数据库中,但是却告诉mysql那是latin1字符集。而GB字符集是一个汉字占两个 字节,latin1是一个字符占一个字节。也就是说一个GB汉字被当成两个latin1字符来保存了。这让我想起了当初的iso8859_1,也是类似的 情况。只要我们保存和读取时都当作latin1,不进行转换,然后在显示时当作gb字符集,就能够正确使用。

遇到的问题:

  • 当数据库为latin1、cp850或koi8r等等,character_set_clientcharacter_set_conn 都为utf8时,执行insert into <Table> values (“测试”),会出现警告:Warning | 1366 | Incorrect string value … , 然后character_set_results 无论设置成什么都无法在获取数据时正确显示中文。

    想想也对:因为latin1、cp850、koi8r这些字符集没有中文,强行把utf8的中文转成这些字符集,应该是转成了一些乱码字符,然后这些乱码字符都永远无法正确的转换回原来正确的字符集了。

你可能感兴趣的:(mysql,学习,笔记,字符集)