10.全球化
本章主要介绍全球化,包含国际化和本地化,的一些问题:
· MySQL在语句中支持的字符集
· 如何为服务配置不同的字符集
· 选择错误信息的语言
· 如何设置服务的时区和每个连接的时区
· 选择本土化的日期和月份名
10.1.3.6 National Character Set
10.1.5.3 在Mysql配置的时候设置字符集和排序规则
10.1.7.9 排序规则和INFORMATION_SCHEMA查找
10.1.8 字符目录(String Repertoire)
10.1.9.3 SHOW语句和INFORMATION_SCHEMA
10.6.2 时区闰秒支持(Time Zone Leap Seconad Support)
10.1 字符集的支持
Mysql可以让你使用不同的字符集来保存数据,可以再多个排序规则下比较数据。可以再服务,数据库,表和列级别设置字符集。本章回答以下几个问题:
· 使用什么字符集和排序规则
· 多个级别的系统默认字符集的分配
· 设置字符集和排序规则的设置语法
· 会收到影响的函数和操作
· Unicode 的支持
· 可用的字符集和排序规则
字符集不单单影响数据存储,也会影响client和server之间的交互。可以使用set names来设置client和server之间交互使用的字符集。
10.1.1 字符集和排序规则
字符集是一堆符号和编码,排序规则是字符集的比较规则。比如比较“A”和“B”最简单的方法是比较编码,这种排序规则也是最简单的排序规则,成为binary排序规则。
对于a=A,b=B我们成为大小写不敏感的排序规则。每个字符集有很多个排序规则。
mysql可以做到以下一些:
1.可以以多个字符集保存字符串
2.比较字符串可以使用不同的排序规则
3.在同一个服务中可以混合多个字符集和排序规则
4.字符集和排序规则有很多级别(server,db,tb.column)
10.1.2 mysql中的字符集和排序规则
Mysql可以支持多个字符集,可以使用SHOW CHARACTER SET来显示。一个字符集有多个排序规则,至少一个,可以使用SHOW COLLATION,查看。
排序规则有以下一些特性:
· 2个不同的字符集不可能拥有同一个排序规则
· 每个字符集都有一个默认排序规则
· 排序规则命名,一般是字符集名,语言名,后缀。后缀有_ci(大小写不明感),_cs(大小写敏感),_bin(binary)
避免使用错的排序规则,保证制定的排序规则给出的顺序是你要的。
10.1.3 制定字符集和排序规则
在4个级别上会设置默认的字符集和排序规则,CHARACTER SET可以用来设置指定字符集。CHARSET是CHARACTER SET的另外一种写法。
字符集不单单影响数据存储,也会影响客户端和服务器之间的交互。
10.1.3.1 服务字符集和排序规则
服务级别的字符集和排序规则可以在命令行和配置文件中设置。使用—character-set-server设置字符集,--collation-server设置排序规则。
shell> mysqld
shell> mysqld --character-set-server=latin1
shell> mysqld --character-set-server=latin1
--collation-server=latin1_swedish_ci
如果要修改默认的服务级的字符集和排序规则,可以通过重编译源代码进行。
shell> cmake . -DDEFAULT_CHARSET=latin1
或
shell> cmake . -DDEFAULT_CHARSET=latin1
-DDEFAULT_COLLATION=latin1_german1_ci
Mysqld和CMake都会验证字符集和排序规则是否可用,如果不可用每个程序都会显示错误信息和中断。
当create database没有指定,会使用服务的字符集和排序规则。Character_set_server和collation_server指定了服务的字符集和排序规则。这些变量可以再运行时被修改。
10.1.3.2 数据库级字符集和排序规则
数据库级的排序规则可以使用create database和alterdatabase来指定。
CREATE DATABASE db_name
[[DEFAULT] CHARACTER SET charset_name]
[[DEFAULT] COLLATE collation_name]
ALTER DATABASE db_name
[[DEFAULT] CHARACTER SET charset_name]
[[DEFAULT] COLLATE collation_name]
CHARACTER_SET,COLLATE子句可以指定不同于服务的字符集和排序规则。
CREATE DATABASE db_name CHARACTER SET latin1 COLLATE latin1_swedish_ci;
Mysql以以下方式来获取字符集和排序规则:
· 如果指定了CHARACTER SET X和COLLATE Y,那么会使用字符集x,排序规则y
· 如果指定了字符集x没有指定排序规则,那么使用默认的排序规则。
· 如果指定了排序规则没有指定字符集,那么使用和排序规则相关的字符集。
· 否则使用服务的字符集和排序规则。
和创建数据库一样,创建表的时候没有指定就使用数据库级的字符集和排序规则。Character_set_database和collation_database系统变量,当有数据库时,为数据库的字符集和排序规则,如果没有数据库时是server的字符集和排序规则。
10.1.3.3 表的字符集和排序规则
表的排序规则可以通过create table和alter table来修改。
CREATE TABLE tbl_name (column_list)
[[DEFAULT] CHARACTER SET charset_name]
[COLLATE collation_name]]
ALTER TABLE tbl_name
[[DEFAULT] CHARACTER SET charset_name]
[COLLATE collation_name]
表选择字符集和排序规则和数据库一致。
如果创建表时,表没有指定列的字符集和排序规则,那么就用表的字符集和排序规则。
10.1.3.4 列的字符集和排序规则
列的字符集和排序规则通过create table和alter table来指定。
col_name {CHAR | VARCHAR | TEXT} (col_length)
[CHARACTER SET charset_name]
[COLLATE collation_name]
col_name {ENUM | SET} (val_list)
[CHARACTER SET charset_name]
[COLLATE collation_name]
如:
CREATE TABLE t1
(
col1 VARCHAR(5)
CHARACTER SET latin1
COLLATE latin1_german1_ci
);
ALTER TABLE t1 MODIFY
col1 VARCHAR(5)
CHARACTER SET latin1
COLLATE latin1_swedish_ci;
Mysql为列选择字符集和排序规则的方法和表一样。
如果alter table从一个字符集转为另外一个字符集的时候,如果字符集不兼容那么就有可能丢失数据。
10.1.3.5 字符串常量的字符集和排序规则
每个字符串都有一个字符集和排序规则。指定字符串常量字符集的方法:
[_charset_name]'string' [COLLATE collation_name]
例如:
SELECT 'string';
SELECT _latin1'string';
SELECT _latin1'string' COLLATE latin1_danish_ci;
如果什么都没有指定,那么使用连接的字符集和排序规则,character_set_connection和collation_connection决定。
Mysql选择字符集和排序规则的方式如下:
· 如果指定了字符集x和排序规则y,那么就是用x,y
· 如果指定了字符集没有指定排序规则,那么使用x和x的默认排序规则
· 否则使用变量character_set_connection和collation_connection
前面指定的字符集并不影响转移字符,转移字符是受character_set_connection影响。
如
mysql> SET NAMES latin1;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT HEX('à '), HEX(_sjis'à ');
+------------+-----------------+
| HEX('à ') | HEX(_sjis'à ') |
+------------+-----------------+
| E00A | E00A |
+------------+-----------------+
1 row in set (0.00 sec)
mysql> SET NAMES sjis;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT HEX('à '), HEX(_latin1'à ');
+------------+-------------------+
| HEX('à ') | HEX(_latin1'à ') |
+------------+-------------------+
| E05C6E | E05C6E |
+------------+-------------------+
1 row in set (0.04 sec)
在字符集sjis中“”并不认为是转移“”=5C,“n”=6E。
10.1.3.6 National Character Set
标准SQL定义NCHAR或者 NATIONAL CHAR来说明这个char列要使用预定于的字符集。Mysql 5.6使用utf8作为预定义的字符集。可以通过N’some text’来创建national 字符集内的字符串。
10.1.3.7 字符集和排序规则设置的例子
1.设置列和表的字符集和排序规则
CREATE TABLE t1
(
c1 CHAR(10) CHARACTER SET latin1 COLLATE latin1_german1_ci
) DEFAULT CHARACTER SET latin2 COLLATE latin2_bin;
2.列不带排序规则
CREATE TABLE t1
(
c1 CHAR(10) CHARACTER SET latin1
) DEFAULT CHARACTER SET latin1 COLLATE latin1_danish_ci;
3.列不带字符集和排序规则
CREATE TABLE t1
(
c1 CHAR(10)
) DEFAULT CHARACTER SET latin1 COLLATE latin1_danish_ci;
4.数据库设置排序规则和字符集,表和列不设置
CREATE DATABASE d1
DEFAULT CHARACTER SET latin2 COLLATE latin2_czech_ci;
USE d1;
CREATE TABLE t1
(
c1 CHAR(10)
);
10.1.3.8 和其他数据的兼容
略
10.1.4 连接的字符集和排序规则
连接的字符集和排序规则主要说明一下一些问题:
· 当客户端发送前语句的时候使用什么字符集,character_set_client
· 语句在传输过程中是什么字符集,character_set_connection,和排序规则collation_connection,然后会从character_set_client转化为character_set_connection。
· 在传输错误或者结果到客户端前字符集是什么,character_set_results指定了返回前的字符集。
有2个语句会影响和连接相关的字符集变量
· SET NAMES ‘charset_name’[COLLATE ‘collation_name’]
SET NAMES相当于以下3个语句
SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET character_set_connection = charset_name;
· 使用SET语句设置,如:
SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET collation_connection = @@collation_database;
Mysql的一些客户端程序如mysql,MySQLadmin,mysqlcheck,mysqlimport,mysqlshow决定默认字符集规则如下:
· 在没有信息的情况下使用Latin1字符集
· 程序可以自动识别OS上使用的字符集,如LANG或者LC_ALL。或者windows上的codepage来判断
· 程序也支持使用—default-character-set选项,显示的设置字符集。
在程序连接服务的时候,使用程序要的字符集发送给服务,然后何止,character_set_client,character_set_results和character_set_connection系统变量。事实上可以使用set names来设置。
如果默认的和你想要的不一样可以使用set names来设置或者加上参数—default-caracter-set,或者加在配置文件中。
[mysql]
default-character-set=koi8r
如果可以自动重连,可以使用charset命令:
mysql> charset utf8
Charset changed
Charset命令使用set names,然后修改mysql的默认字符集,然后重连。
假设列column1定义为char(5) CHARACTER SET latin2。如果没有设置set names和set character set。那么mysql会以column1的字符集和排序规则来发送数据,如果设置了set names latin1,那么会把latin2转化为latin1然后再发送。
如果要尝试直接发送:
SET character_set_results = NULL;
10.1.5 为应用程序配置字符集和排序规则
应用程序存数据使用默认的字符集和排序规则。如果应用程序要不同的排序规则和字符集,可以通过几个方式来配置:
· 为每个数据库指定一个字符集
· 在服务启动的时候指定一个字符集
· 在mysql编译的时候配置字符集
如果所有的或者大多数的应用程序使用相同的字符集,可以再服务启动的时候或者配置mysql的时候设置。
10.1.5.1 为每个数据指定字符集和排序规则
在create database中设置如:
CREATE DATABASE mydb
DEFAULT CHARACTER SET utf8
DEFAULT COLLATE utf8_general_ci;
应用程序在使用这个数据库的时候,每次连接都要配置可以指定—default-character-set=utf8或者set names=‘utf8’。
如果修改了数据库的字符集和排序规则,那么所有的过程都要被重建。
10.1.5.2 在服务启动是配置字符集
在服务启动的时候,使用命令行—character-set-server和—collation-server,或者设置配置文件:
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
这些配置是服务级别的,所以在创建数据库的时候会以这个为默认的字符集和排序规则。一旦设置了之后,每个连接可能都要设置一下排序规则,那么可以再系统启动的时候设置—init_connect=”SET NAMES ‘utf8’”,这样每次连接的时候都会运行一下set names命令。
10.1.5.3 在Mysql配置的时候设置字符集和排序规则
设置方法:
shell> cmake . -DDEFAULT_CHARSET=utf8
-DDEFAULT_COLLATION=utf8_general_ci
10.1.6 错误信息的字符集
在mysql 5.6,服务使用utf8构建消息,并且通过character_set_results系统变量来返回。
服务构建错误消息的过程:
· 消息模板使用utf8
· 然后消息中的参数被替换
o 如表名,列名会被原样复制
o 如果是字符串那么会被转化成utf8
o 如果是二进制串恢复0x20到0x7e之间的值,其他的会被加上前缀x
o Duplicate entry 'AxC3x9F' for key 1
如果构建完就会发送给client,字符集会从utf8转化为character_set_results系统变量。如果变量为null或者binary不会发送转化。
如果字符在character_set_results上没办法体现,某些字符可能发生某些编码。编码使用unicode指针值:
· 如果字符在BMP(Basic Multilingual Plane)范围内(0x0000-0xffff)写成 nnn
· 如果再范围之外室友+nnnnnn
客户端可以控制character_set_results来控制接受的字符串的字符集。
10.1.7 排序规则
10.1.7.1 排序规则名
排序规则名一般以_ci,_cs,_bin结尾
10.1.7.2 在语句中使用排序规则
· With ORDER BY:
SELECT k
FROM t1
ORDER BY k COLLATE latin1_german2_ci;
· With AS:
SELECT k COLLATE latin1_german2_ci AS k1
FROM t1
ORDER BY k1;
· With GROUP BY:
SELECT k
FROM t1
GROUP BY k COLLATE latin1_german2_ci;
· With aggregate functions:
SELECT MAX(k COLLATE latin1_german2_ci)
FROM t1;
· With DISTINCT:
SELECT DISTINCT k COLLATE latin1_german2_ci
FROM t1;
· With WHERE:
SELECT *
FROM t1
WHERE _latin1 'Müller' COLLATE latin1_german2_ci = k;
SELECT *
FROM t1
WHERE k LIKE _latin1 'Müller' COLLATE latin1_german2_ci;
· With HAVING:
SELECT k
FROM t1
GROUP BY k
HAVING k = _latin1 'Müller' COLLATE latin1_german2_ci;
10.1.7.3 COLLATE子句
COLLATE比||优先级要高,所以下面语句是等价的:
x || y COLLATE z
x || (y COLLATE z)
10.1.7.4 排序规则必须要一个正确的字符集
一个字符集有多个排序规则,但是一个排序规则只能对应到一个字符集,因此如果出现以下情况那么就会报错:
mysql> SELECT _latin1 'x' COLLATE latin2_bin;
ERROR 1253 (42000): COLLATION 'latin2_bin' is not valid
for CHARACTER SET 'latin1'
10.1.7.5 表达式中的排序规则
排序规则的主要用处是用来比较,如:
SELECT x FROM T ORDER BY x;
SELECT x FROM T WHERE x = x;
SELECT DISTINCT x FROM T;
如果是2个不同的操作数,可能排序规则会歧义如:
SELECT x FROM T WHERE x = 'Y';
出现这种情况标准的sql使用可压缩性规则来确定使用什么字符集。可压缩性值有以下决定:
· 一个现实的COLLATE子句的可压缩性值为0
· 并列的2个字符串有2个不同的排序规则,那么可压缩性为1
· 列或者存储的参数的排序规则可压缩性为2
· 系统常量,或者USER(),VERSION()的可压缩性为3
· 字符串常量的可压缩性值为4
· Null的可压缩性值为5
Mysql使用可压缩性值来解决这个问题:
· 使用可压缩值最低的
· 如果吹按可压缩值相同
o 如果2边都是unicode或者都不是那么就报错
o 如果一边是unicode,一边不是那么传为非unicode
o 如果操作字符集相同,但是排序规则不同,比如_bin混合了_ci,_cs那么使用_bin。
可压缩性值,可以通过coercibility查看
对于使用concat隐式转化的字符集和排序规则有连接的字符集和排序规则决定。
10.1.7.6 _bin和binary
串有2中,一种是字符串(Nonbinary string),一种是二进制串(binary string)。
Nonbinary string以数据类型char,varchar,text保存,有字符集和排序规则。
Binary string以binary,varbinary,blob类型保存,无字符集和排序规则。
在很多方面_bin排序规则和binary排序规则不同。
10.1.7.6.1 比较单位
Binary string是字节流。比较是根据字节的值来比较。Nonbinary string是字符流,所以比较是以一个字符来进行比较。Nonbinary strings的排序规则定义了字符的顺序和比较。对于_bin排序规则就是以字符的二进制的值进行排序。
10.1.7.6.2 字符集转化
Nonbinary strings可以转化为其他字符集的字符串,就算是_bin排序规则的。但是对于binary strings就不会发生转化。
10.1.7.6.3 大小写转化
Nonbinary strings可以大小写转化:
mysql> SET NAMES latin1 COLLATE latin1_bin;
Query OK, 0 rows affected (0.02 sec)
mysql> SELECT LOWER('aA'), UPPER('zZ');
+-------------+-------------+
| LOWER('aA') | UPPER('zZ') |
+-------------+-------------+
| aa | ZZ |
+-------------+-------------+
1 row in set (0.13 sec)
在binary strings中是不能转化的:
mysql> SET NAMES binary;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT LOWER('aA'), LOWER(CONVERT('aA' USING latin1));
+-------------+-----------------------------------+
| LOWER('aA') | LOWER(CONVERT('aA' USING latin1)) |
+-------------+-----------------------------------+
| aA | aa |
+-------------+-----------------------------------+
1 row in set (0.00 sec)
10.1.7.6.4 比较时尾随的空格处理
Nonbinary strings所有的排序规则有PADSPACE行为。
mysql> SET NAMES utf8 COLLATE utf8_bin;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT 'a ' = 'a';
+------------+
| 'a ' = 'a' |
+------------+
| 1 |
+------------+
1 row in set (0.00 sec)
但是binary string不行
mysql> SET NAMES binary;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT 'a ' = 'a';
+------------+
| 'a ' = 'a' |
+------------+
| 0 |
+------------+
1 row in set (0.00 sec)
10.1.7.6.5 插入和获取时的空格处理
CHAR(N)保存nonbinary strings数据,当插入长度不足N会以空格插入。
binary(N)保存binary string数据,会以0x00填充。
mysql> CREATE TABLE t1 (
-> a CHAR(10) CHARACTER SET utf8 COLLATE utf8_bin,
-> b BINARY(10)
-> );
Query OK, 0 rows affected (0.09 sec)
mysql> INSERT INTO t1 VALUES ('a','a');
Query OK, 1 row affected (0.01 sec)
mysql> SELECT HEX(a), HEX(b) FROM t1;
+--------+----------------------+
| HEX(a) | HEX(b) |
+--------+----------------------+
| 61 | 61000000000000000000 |
+--------+----------------------+
1 row in set (0.04 sec)
10.1.7.7 BINARY操作
Binary操作是把它之后的字符串变为binary string。BINARY str是cast(str as BINARY)的缩写。
10.1.7.8 Collation的影响例子
略
10.1.7.9 排序规则和INFORMATION_SCHEMA查找
INFORMATION_SCHEMA表的字符串列的排序规则是uft8_genernal_ci,大小写不敏感。但是文件系统的是否大小写敏感也会影响INFORMATION_SCHEMA的字符串列。如linux系统上大小写是敏感的。
在linux 下:
mysql> SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
-> WHERE SCHEMA_NAME = 'test';
+-------------+
| SCHEMA_NAME |
+-------------+
| test |
+-------------+
1 row in set (0.01 sec)
mysql> SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
-> WHERE SCHEMA_NAME = 'TEST';
Empty set (0.00 sec)
在windows下:
mysql> SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
-> WHERE SCHEMA_NAME = 'test';
+-------------+
| SCHEMA_NAME |
+-------------+
| test |
+-------------+
1 row in set (0.00 sec)
mysql> SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
-> WHERE SCHEMA_NAME = 'TEST';
+-------------+
| SCHEMA_NAME |
+-------------+
| TEST |
+-------------+
1 row in set (0.00 sec)
如果列的排序规则和想到的不同,可以使用COLLATE语句显示指定一个排序规则:
mysql> SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
-> WHERE SCHEMA_NAME COLLATE utf8_general_ci = 'test';
+-------------+
| SCHEMA_NAME |
+-------------+
| test |
+-------------+
1 row in set (0.00 sec)
mysql> SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA
-> WHERE SCHEMA_NAME COLLATE utf8_general_ci = 'TEST';
| SCHEMA_NAME |
+-------------+
| test |
+-------------+
1 row in set (0.00 sec)
当然也可以使用UPPER(),LOWER()。
10.1.8 字符目录(String Repertoire)
字符集目录是字符集合。
字符串表达式有2种目录属性:
ASCII:只能包含unicode范围中U+0000到U+007U。
UNICODE:包含unicode范围从U+0000到U+FFFF。
ASCII是UNICODE的子集。
使用以下例子来说明目录:
· 字符串的内容限制了目录
SET NAMES utf8; SELECT 'abc';
SELECT _utf8'def';
SELECT N'MySQL';
尽管是使用了utf8字符集,但是只使用了ASCII范围的所以目录是ASCII。不是unicode。
· 有ascii字符集的列有ASCII目录。2个列concat的时候只能是子集连接超集,如:
CREATE TABLE t1 (
c1 CHAR(1) CHARACTER SET latin1,
c2 CHAR(1) CHARACTER SET ascii
);
INSERT INTO t1 VALUES ('a','b');
SELECT CONCAT(c1,c2) FROM t1;
ERROR 1267 (HY000): Illegal mix of collations (latin1_swedish_ci,IMPLICIT)
and (ascii_general_ci,IMPLICIT) for operation 'concat'
如果是子集连接超集。
+---------------+
| CONCAT(c1,c2) |
+---------------+
| ab |
+---------------+
· 有一个字符串参数的函数,会集成参数的目录,如UPPER(_utf8’abc’)是ASCII目录。
· 如果函数返回字符串,但是没有字符串参数,那么字符集使用chararter_set_connection作为结果,如果字符集是ascii那么目录使用ascii,否则使用unicode。
· 当函数有2个或者2个以上字符参数时,使用最宽的目录,如unicode比ascii宽,
CONCAT(_ucs2 0x0041, _ucs2 0x0042)
CONCAT(_ucs2 0x0041, _ucs2 0x00C2)
第一个函数都没超过ascii range,那么就是ascii,第二个超过了ascii,那么就是unicode。
· 函数的返回字符串的目录由参数的目录决定
10.1.9 受字符集影响的操作
10.1.9.1 结果字符串
Mysql有很多操作和函数返回字符串。那么本节介绍结果字符串的排序规则和字符集。
对于简单的函数,输入字符串的,那么输出的字符串和输入的字符串的排序规则和字符集一样。
如果输入或者输出的字符串是binary串,那么是没有字符集和排序规则的,charset()和collation()都是返回binary。
如果有对个数据字符串,一个输出,那么有以下规则决定输出的排序规则:
· 如果现实的指定了collate x那么使用x
· 如果指定了collate x,collate y,报错
· 如果所有的排序规则是x,那么使用x
· 否则结果没有排序规则
10.1.9.2 CONVERT()和CAST()
Convert提供了字符串在不同字符集上转化的功能
CONVERT(expr USING transcoding_name)
如:
SELECT CONVERT(_latin1'Müller' USING utf8);
INSERT INTO utf8table (utf8column)
SELECT CONVERT(latin1field USING utf8) FROM latin1table;
也可以使用cast,
CAST(character_string AS character_data_type CHARACTER SET charset_name)
如:
SELECT CAST(_latin1'test' AS CHAR CHARACTER SET utf8);
Convert和cast上不能使用字符集,可以再函数外面使用:
SELECT CAST(_latin1'test' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin;
10.1.9.3 SHOW语句和INFORMATION_SCHEMA
show命令的结果会提供一些字符集合排序规则信息,INFORMATION_SCHEMA和show命令类似。
10.1.10 Unicode支持
起初,mysql 4.1中就支持2中保存unicode的方式:
· Ucs2:每个字节使用2个字节来编码。
· Utf8:每个字节1-3个字节。
这2个字符集支持来自unicode 3.0 BMP的字符。
如果超出BMP的字符会被替换为‘?’
在mysql 5.6,为了要支持新的unicode的字符,所以增加了新的字符集。
Mysql 5.6支持的字符集:
Ucs2:有2个字节来编码
Utf16:和ucs2类似,但是多了新增的字符
Utf16le:utf16是大端的,utf16le是小端的
Utf32:每个字符3个字节
Utf8:每个字符1-3个字节
Utf8mb4:1-4个字节编码
Ucs2,utf8支持BMP字符,utf8mb4,utf16,utf16le和utf32支持BMP字符和额外字符。
10.1.10.1 ucs2字符集
在ucs2字符集中,每个字符占2个字节。
10.1.10.2 utf16字符集
Utf16是ucs2加上一些新增的字符,有以下 特性:
· 对于BMP字符,utf16个ucs2编发都是一样的
· 对于增益的字符,如果大于0xffff,加上0xd800,放到前16bit,然后去之后的10bit加上0xdc00放到后16bit。
10.1.10.3 utf16le字符集
和utf16一样,只是是小端的。
10.1.10.4 utf32字符集
Utf32的字符长度是固定的,4个字节。在使用的时候要注意,定义的长度必须是4字节的倍数。
10.1.10.5 utf8字符集
Uft8是一种保存unicode的替代方案。
Utf8把字符以不同的字节长度进行编码:
· 基本的Latin字母,数字和标点符号使用一个字节。
· 大多数欧洲,中东字符使用2个字节。
· 韩文,中文,日文在3个或者4个字节中。
Utf8在mysql5.6还是之前的版本都有2个特点:
· 不支持新增字符。
· 最多每个字符3个字节。
Uft8和ucs2有相同的目录
10.1.10.6 utf8mb3字符集
在未来utf8会变成4字节的utf8,之前的3字节的utf8要被称为utf8mb8。为了避免将来复制之间的版本不同导致字符集问题,可以让用户指定utf8mb3字符集和排序规则。如:
CREATE TABLE t (s1 CHAR(1) CHARACTER SET utf8mb3;
SELECT * FROM t WHERE s1 COLLATE utf8mb3_general_ci = 'x';
DECLARE x VARCHAR(5) CHARACTER SET utf8mb3 COLLATE utf8mb3_danish_ci;
SELECT CAST('a' AS CHAR CHARACTER SET utf8) COLLATE utf8_czech_ci;
这时,mysql会吧utf8mb3转化为utf8.目前utf8mb3只能用在CHARACTER SET子句上。
10.1.10.7 utf8mb4字符集
Utf8字符集一个字符最多3个字节,并且只包含BMP字符。Utf8mb4是最多使用每个字符4个字节:
· 对于BMP,utf8和utf8mb4都是一样的存储特性,同样的code值,同样的编码,同样的长度。
· 对于新增的字符,utf8buneng baocun ,utf8mb4需要4个字节来保存。
Utf8mb4是utf8的超集。
10.1.11 更新unicode的支持
对于utf8mb4个utf8比,在相同的长度下utf8能够存更多的字符。可以使用alter table把utf8转成utf8mb4:
CREATE TABLE t1 (
col1 CHAR(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
col2 CHAR(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL
) CHARACTER SET utf8;
ALTER TABLE t1
DEFAULT CHARACTER SET utf8mb4,
MODIFY col1 CHAR(10)
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
MODIFY col2 CHAR(10)
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL;
对于BMP字符来说,utf8和utf8mb4没有什么差别,但是utf8不支持新增字符。
对于所有的char,varchar,text需要注意:
· 检查所有的utf8定义的列,不会超过引擎的最大值
· 检查utf8的索引,不会超过引擎的最大值。
如果上面条件不满足,那么只能使用utf8.还有一些例子修改字符集可能要修改表结构:
· Tinytext类型,只有255个字节,如果使用3个字节一个字符最多可以存85个,如果4字节那么最多63个。如果要使用utf8mb4又要超过63个字符,那么需要修改表结构。
· Innodb索引,如果使用COMPACT或REDUNDANT行格式,最长767个字节。对于utf8和utf8mb4来说,最多可以保存255和191个字符。也就是说,如果utf8长度超过191个字符,要转化为utf8mb4那么是不够的。
还有一些utf8和utf8mb4的比较:
· 4字节的utf8比3字节的utf8性能差
· Set names ‘utf8mb4’连接字符集会是4字节字符集,如果没有4个字节字符发送就没有问题。否则应用程序视图以最大3个字节获取信息可能会出问题。
· 应用不能发utf16,utf16le,utf32发送数据到老的服务
· 对于复制,如果master支持新增的字符集,那么slave必须也支持。
如果要从新系统换成老系统:
· Ucs2和utf8是没问题的
· 老系统不支持utf8mb4,utf16,utf16le,utf32
· 对于使用了utf8mb4的对象定义,可以先dump,然后把utf8mb4转为utf8。
10.1.12 元数据为utf8
元数据需要满足以下特点:
· 所有的元数据要使用同一个字符集。
· 元数据必须包含所有语言的字符。
为了满足这个条件,mysql使用utf8字符集来存元数据。比如函数USER(),CURRENT_USER(),SESSION_USER(),SYSTEM_USER(),DATEBASE()和VERSION(),默认都是使用utf8。
使用character_set_system系统变量来控制元数据的字符集。
元数据是以character_set_system保存的,并不意味着DESCRIBE函数返回的字符集也是默认character_set_system字符集。如select column1 from t,从服务器返回给client,column1的字符集是由character_set_results决定的,默认为latin1。如果想要把元数据的结果以其他字符集传输,可以使用set name 来设置。如果character_set_results为null,那么就不会发出转化,如果是元数据,那么会以character_Set_system字符集传输。
如果在表达式中使用user()那么mysql会自动进行传化。
SELECT * FROM t1 WHERE USER() = latin1_column;
INSERT INTO t1 (latin1_column) SELECT USER();
10.1.13 列字符集转化
使用alter table把binary string或者nonbinary string 指定特定字符集。
· 如果列是bianry数据类型(BINARY,VARBINARY,BLOB)那么所有的值只能是一个字符集。
· 如果是nonbinary数据类型(char,varchar,text)包含的值,只能变编码为列自定的字符集。
假设表t是有一个col1定义为varbinary,使用一个字符集编码,并且为greek,那么可以通过以下语句去转化:
ALTER TABLE t MODIFY col1 VARCHAR(50) CHARACTER SET greek;
如果类型是binary(50),那么可以转化为char(50)但是,可能会出现0x00在末尾可能无法描述,需要使用trim();
UPDATE t SET col1 = TRIM(TRAILING 0x00 FROM col1);
如果表t有col1 char(50) character set latin1,要转化为utf8,可以直接:
ALTER TABLE t MODIFY col1 CHAR(50) CHARACTER SET utf8;
10.1.14 MySQL支持的字符集和排序规则
略
10.2 设置错误信息语言
默认mysql的错误信息是英语,但是可以指定其他语言。
在mysql 5.6中错误信息在以下2个目录下去查找:
· 会从lc_messages_dir和lc_messages 2个系统变量上去查找,使用如下:
shell> mysqld --lc_messages_dir=/usr/share/mysql --lc_messages=fr_FR
· 如果找不到,那么会放弃lc_messages,直接通过lc_messages_dir来查找。
lc_messages_dir是全局只读变量,lc_messages是全局和会话变量,可以运行时修改。默认语言文件在share/mysql/LANGUAGE目录下。
10.3 增加一个字符集
略
10.4 为字符集增加一个排序规则
略
10.5 配置字符集
略
10.6 MySQL时区的支持
mysql时区有好几个维护时区的设置:
· 系统级别的时区,当服务启动的时候,把系统使用的时区赋值给system_time_zone系统变量
· 服务当前时区,全局time_zone,表明当前所在的时区,初始值是’SYSTEM’表示和服务时区一样。全局服务时间可以在启动时设置,可以使用--default-time-zone。
· 每个连接的时区,当连接的时候会话变量time_zone和全局time_zone一样,但是可以自己修改。
当前的时区,不会对UTC_TIMESTAMP()和date,time,datetime列的值进行影响。当前的时区可以通过以下方式获取:
mysql> SELECT @@global.time_zone, @@session.time_zone;
时区的值可以是以下几种:
· ‘SYSTEM’表示和服务的时区一样
· ‘+10:00’,’-6:00’直接设置时区
· 使用命名的时区,如 ‘Europe/Helsinki’
mysql安装的时候会生成时区表在mysql数据库内,但是不会被load需要手动load。
如果系统有自己的时区信息,那么可以使用mysql_tzinfo_to_sql程序来填充时区表,如linux,时区信息在/usr/shar/zoneinfo目录下。如果没有时区库,那么可以自己去下载。
mysql_tzinfo_to_sql用来把时区信息导入到时区表中:
shell> mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql
10.6.1 跟上时区修改
当时区被修改的时候,应用程序还是使用老的时区,为了跟上,确定系统使用的时区信息是很有必要的。对于mysql有2个方式可以用于跟上时区:
· 如果时区设置为‘system’那么操作系统的时区会影响mysql服务的事务。
· 如果实在/ect/localtime时区文件中替换,你就应该重启mysqld才能更新修改后的时区。
如果使用命名时区,那么要保证时区表中的数据时最新的。若系统有自己的时区信息,那么系统的时区信息被更新的时候,要保证mysql内的时区信息也要被更新。mysqld会cache时区信息因此更新后需要重启服务。如果你要使用命名时区但是不确定有没有,那么先查看时区表。
mysql> SELECT COUNT(*) FROM mysql.time_zone_name;
+----------+
| COUNT(*) |
+----------+
| 0 |
+----------+
若不为空那么说明可以使用命名时区。
检查是否有夏令时规则:
SELECT CONVERT_TZ('2007-03-11 2:00:00','US/Eastern','US/Central');
SELECT CONVERT_TZ('2007-03-11 3:00:00','US/Eastern','US/Central');
更新前:
mysql> SELECT CONVERT_TZ('2007-03-11 2:00:00','US/Eastern','US/Central');
+------------------------------------------------------------+
| CONVERT_TZ('2007-03-11 2:00:00','US/Eastern','US/Central') |
+------------------------------------------------------------+
| 2007-03-11 01:00:00 |
+------------------------------------------------------------+
mysql> SELECT CONVERT_TZ('2007-03-11 3:00:00','US/Eastern','US/Central');
+------------------------------------------------------------+
| CONVERT_TZ('2007-03-11 3:00:00','US/Eastern','US/Central') |
+------------------------------------------------------------+
| 2007-03-11 02:00:00 |
+------------------------------------------------------------+
更新后:
mysql> SELECT CONVERT_TZ('2007-03-11 2:00:00','US/Eastern','US/Central');
+------------------------------------------------------------+
| CONVERT_TZ('2007-03-11 2:00:00','US/Eastern','US/Central') |
+------------------------------------------------------------+
| 2007-03-11 01:00:00 |
+------------------------------------------------------------+
mysql> SELECT CONVERT_TZ('2007-03-11 3:00:00','US/Eastern','US/Central');
+------------------------------------------------------------+
| CONVERT_TZ('2007-03-11 3:00:00','US/Eastern','US/Central') |
+------------------------------------------------------------+
| 2007-03-11 01:00:00 |
+------------------------------------------------------------+
10.6.2 时区闰秒支持(Time Zone Leap Seconad Support)
如果出现闰秒(23:59:60)情况会使用59:59。
mysql> CREATE TABLE t1 (
-> a INT,
-> ts TIMESTAMP DEFAULT NOW(),
-> PRIMARY KEY (ts)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> -- change to UTC
mysql> SET time_zone = '+00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> -- Simulate NOW() = '2008-12-31 23:59:59'
mysql> SET timestamp = 1230767999;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 (a) VALUES (1);
Query OK, 1 row affected (0.00 sec)
mysql> -- Simulate NOW() = '2008-12-31 23:59:60'
mysql> SET timestamp = 1230768000;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 (a) VALUES (2);
Query OK, 1 row affected (0.00 sec)
mysql> -- values differ internally but display the same
mysql> SELECT a, ts, UNIX_TIMESTAMP(ts) FROM t1;
+------+---------------------+--------------------+
| a | ts | UNIX_TIMESTAMP(ts) |
+------+---------------------+--------------------+
| 1 | 2008-12-31 23:59:59 | 1230767999 |
| 2 | 2008-12-31 23:59:59 | 1230768000 |
+------+---------------------+--------------------+
2 rows in set (0.00 sec)
mysql> -- only the non-leap value matches
mysql> SELECT * FROM t1 WHERE ts = '2008-12-31 23:59:59';
+------+---------------------+
| a | ts
+------+---------------------+
| 1 | 2008-12-31 23:59:59 |
+------+---------------------+
1 row in set (0.00 sec)
mysql> -- the leap value with seconds=60 is invalid
mysql> SELECT * FROM t1 WHERE ts = '2008-12-31 23:59:60';
Empty set, 2 warnings (0.00 sec)
10.7 MySQL Server本地化支持
lc_time_names控制了日期,月份星期的显示语言,会影响DATA_FORMAT(),DAYNAME(),MONTHNAME()的输出。但是lc_time_names不影响STR_TO_DATE(),GET_FORMAT()。format虽然不受lc_time_names影响,但是通过传入参数可以影响结果。
mysql> SET NAMES 'utf8';
Query OK, 0 rows affected (0.09 sec)
mysql> SELECT @@lc_time_names;
+-----------------+
| @@lc_time_names |
+-----------------+
| en_US |
+-----------------+
1 row in set (0.00 sec)
mysql> SELECT DAYNAME('2010-01-01'), MONTHNAME('2010-01-01');
+-----------------------+-------------------------+
| DAYNAME('2010-01-01') | MONTHNAME('2010-01-01') |
+-----------------------+-------------------------+
| Friday | January |
+-----------------------+-------------------------+
1 row in set (0.00 sec)
mysql> SELECT DATE_FORMAT('2010-01-01','%W %a %M %b');
+-----------------------------------------+
| DATE_FORMAT('2010-01-01','%W %a %M %b') |
+-----------------------------------------+
| Friday Fri January Jan |
+-----------------------------------------+
1 row in set (0.00 sec)
mysql> SET lc_time_names = 'es_MX';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@lc_time_names;
+-----------------+
| @@lc_time_names |
+-----------------+
| es_MX |
+-----------------+
1 row in set (0.00 sec)
mysql>