MySQL8 中文参考(四十二)
11.4 用户定义变量
您可以在一个语句中将值存储在用户定义变量中,并在另一个语句中引用它。这使您可以从一个语句传递值到另一个语句。
用户变量写作@*var_name*,其中变量名*var_name*由字母数字字符、.、_和$组成。如果要包含其他字符,可以将其引用为字符串或标识符(例如,@'my-var'、@"my-var"或@my-var``)。
用户定义变量是会话特定的。一个客户端定义的用户变量对其他客户端不可见或不可用。(例外:具有对性能模式user_variables_by_thread表的访问权限的用户可以看到所有会话的所有用户变量。)给定客户端会话的所有变量在该客户端退出时会自动释放。
用户变量名称不区分大小写。名称最大长度为 64 个字符。
设置用户定义变量的一种方法是发出SET语句:
SET @*var_name* = *expr* [, @*var_name* = *expr*] ...
对于SET,赋值操作符可以使用=或:=。
用户变量可以被赋予整数、小数、浮点、二进制或非二进制字符串、或NULL值等有限数据类型的值。赋予小数和实数值不会保留值的精度或标度。除了允许的类型之外的类型的值会被转换为允许的类型。例如,具有时间或空间数据类型的值会被转换为二进制字符串。具有JSON数据类型的值会被转换为具有utf8mb4字符集和utf8mb4_bin校对的字符串。
如果用户变量被赋予非二进制(字符)字符串值,则具有与字符串相同的字符集和校对。用户变量的可强制性是隐式的。(这与表列值的可强制性相同。)
赋予用户变量的十六进制或位值被视为二进制字符串。要将十六进制或位值作为数字赋予用户变量,需在数值上下文中使用。例如,添加 0 或使用CAST(... AS UNSIGNED):
mysql> SET @v1 = X'41';
mysql> SET @v2 = X'41'+0;
mysql> SET @v3 = CAST(X'41' AS UNSIGNED);
mysql> SELECT @v1, @v2, @v3;
+------+------+------+
| @v1 | @v2 | @v3 |
+------+------+------+
| A | 65 | 65 |
+------+------+------+
mysql> SET @v1 = b'1000001';
mysql> SET @v2 = b'1000001'+0;
mysql> SET @v3 = CAST(b'1000001' AS UNSIGNED);
mysql> SELECT @v1, @v2, @v3;
+------+------+------+
| @v1 | @v2 | @v3 |
+------+------+------+
| A | 65 | 65 |
+------+------+------+
如果用户变量的值在结果集中被选中,则作为字符串返回给客户端。
如果引用未初始化的变量,则其值为NULL,类型为字符串。
从 MySQL 8.0.22 开始,在准备语句中引用用户变量时,其类型在首次准备语句时确定,并在此后每次执行语句时保留此类型。类似地,在存储过程中的语句中使用的用户变量的类型在首次调用存储过程时确定,并在每次后续调用时保留此类型。
用户变量可以在大多数允许表达式的上下文中使用。目前不包括明确要求字面值的上下文,例如SELECT语句的LIMIT子句,或LOAD DATA语句的IGNORE *N* LINES子句。
MySQL 的先前版本允许在SET之外的语句中为用户变量赋值。这种功能在 MySQL 8.0 中得到支持以保持向后兼容性,但可能在将来的 MySQL 版本中被移除。
在这种方式进行赋值时,必须使用:=作为赋值运算符;=在SET之外的语句中被视为比较运算符。
表达式中涉及用户变量的评估顺序是未定义的。例如,不能保证SELECT @a, @a:=@a+1首先评估@a然后执行赋值。
此外,变量的默认结果类型基于语句开始时的类型。如果变量在语句开始时保存一个类型的值,并在其中分配一个不同类型的新值,则可能会产生意外效果。
为避免此行为问题,在单个语句中不要为同一变量分配值并读取该值,或者在使用之前将变量设置为0、0.0或''以定义其类型。
当在HAVING、GROUP BY和ORDER BY中引用在选择表达式列表中分配值的变量时,不会按预期工作,因为表达式在客户端上评估,因此可能使用来自先前行的过时列值。
用户变量旨在提供数据值。它们不能直接用作 SQL 语句中的标识符或标识符的一部分,例如在期望表或数据库名称的上下文中,或作为保留字,比如SELECT。即使变量被引用,也是如此,如下例所示:
mysql> SELECT c1 FROM t;
+----+
| c1 |
+----+
| 0 |
+----+
| 1 |
+----+
2 rows in set (0.00 sec)
mysql> SET @col = "c1";
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @col FROM t;
+------+
| @col |
+------+
| c1 |
+------+
1 row in set (0.00 sec)
mysql> SELECT `@col` FROM t;
ERROR 1054 (42S22): Unknown column '@col' in 'field list'
mysql> SET @col = "`c1`";
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @col FROM t;
+------+
| @col |
+------+
| `c1` |
+------+
1 row in set (0.00 sec)
不能使用用户变量提供标识符的原则有一个例外,那就是当您构建一个字符串以供稍后执行的准备语句时。在这种情况下,用户变量可以用来提供语句的任何部分。以下示例说明了如何实现这一点:
mysql> SET @c = "c1";
Query OK, 0 rows affected (0.00 sec)
mysql> SET @s = CONCAT("SELECT ", @c, " FROM t");
Query OK, 0 rows affected (0.00 sec)
mysql> PREPARE stmt FROM @s;
Query OK, 0 rows affected (0.04 sec)
Statement prepared
mysql> EXECUTE stmt;
+----+
| c1 |
+----+
| 0 |
+----+
| 1 |
+----+
2 rows in set (0.00 sec)
mysql> DEALLOCATE PREPARE stmt;
Query OK, 0 rows affected (0.00 sec)
更多信息请参见第 15.5 节,“准备语句”。
类似的技术可以在应用程序中使用,以使用程序变量构建 SQL 语句,如下所示使用 PHP 5:
<?php
$mysqli = new mysqli("localhost", "user", "pass", "test");
if( mysqli_connect_errno() )
die("Connection failed: %s\n", mysqli_connect_error());
$col = "c1";
$query = "SELECT $col FROM t";
$result = $mysqli->query($query);
while($row = $result->fetch_assoc())
{
echo "<p>" . $row["$col"] . "</p>\n";
}
$result->close();
$mysqli->close();
?>
以这种方式组装 SQL 语句有时被称为“动态 SQL”。
11.5 表达式
此部分列出了在 MySQL 中表达式必须遵循的语法规则,并提供了关于可能出现在表达式中的术语类型的附加信息。
-
表达式语法
-
表达式术语注释
-
时间间隔
表达式语法
以下语法规则定义了 MySQL 中的表达式语法。这里显示的语法基于 MySQL 源分发中 sql/sql_yacc.yy 文件中给出的语法。有关一些表达式术语的附加信息,请参见表达式术语注释。
*expr*:
*expr* OR *expr*
| *expr* || *expr*
| *expr* XOR *expr*
| *expr* AND *expr*
| *expr* && *expr*
| NOT *expr*
| ! *expr*
| *boolean_primary* IS [NOT] {TRUE | FALSE | UNKNOWN}
| *boolean_primary*
*boolean_primary*:
*boolean_primary* IS [NOT] NULL
| *boolean_primary* <=> *predicate*
| *boolean_primary* *comparison_operator* *predicate*
| *boolean_primary* *comparison_operator* {ALL | ANY} (*subquery*)
| *predicate*
*comparison_operator*: = | >= | > | <= | < | <> | !=
*predicate*:
*bit_expr* [NOT] IN (*subquery*)
| *bit_expr* [NOT] IN (*expr* [, *expr*] ...)
| *bit_expr* [NOT] BETWEEN *bit_expr* AND *predicate*
| *bit_expr* SOUNDS LIKE *bit_expr*
| *bit_expr* [NOT] LIKE *simple_expr* [ESCAPE *simple_expr*]
| *bit_expr* [NOT] REGEXP *bit_expr*
| *bit_expr*
*bit_expr*:
*bit_expr* | *bit_expr*
| *bit_expr* & *bit_expr*
| *bit_expr* << *bit_expr*
| *bit_expr* >> *bit_expr*
| *bit_expr* + *bit_expr*
| *bit_expr* - *bit_expr*
| *bit_expr* * *bit_expr*
| *bit_expr* / *bit_expr*
| *bit_expr* DIV *bit_expr*
| *bit_expr* MOD *bit_expr*
| *bit_expr* % *bit_expr*
| *bit_expr* ^ *bit_expr*
| *bit_expr* + *interval_expr*
| *bit_expr* - *interval_expr*
| *simple_expr*
*simple_expr*:
*literal*
| *identifier*
| *function_call*
| *simple_expr* COLLATE *collation_name*
| *param_marker*
| *variable*
| *simple_expr* || *simple_expr*
| + *simple_expr*
| - *simple_expr*
| ~ *simple_expr*
| ! *simple_expr*
| BINARY *simple_expr*
| (*expr* [, *expr*] ...)
| ROW (*expr*, *expr* [, *expr*] ...)
| (*subquery*)
| EXISTS (*subquery*)
| {*identifier* *expr*}
| *match_expr*
| *case_expr*
| *interval_expr*
关于运算符优先级,请参见第 14.4.1 节,“运算符优先级”。某些运算符的优先级和含义取决于 SQL 模式:
-
默认情况下,
||是逻辑OR运算符。启用PIPES_AS_CONCAT后,||是字符串连接运算符,优先级介于^和一元运算符之间。 -
默认情况下,
!的优先级高于NOT。启用HIGH_NOT_PRECEDENCE后,!和NOT具有相同的优先级。
请参见第 7.1.11 节,“服务器 SQL 模式”。
表达式术语注释
有关文字值语法,请参见第 11.1 节,“文字值”。
有关标识符语法,请参见第 11.2 节,“模式对象名称”。
变量可以是用户变量、系统变量、存储过程本地变量或参数:
-
用户变量:第 11.4 节,“用户定义变量”
-
系统变量:第 7.1.9 节,“使用系统变量”
-
存储过程本地变量:第 15.6.4.1 节,“本地变量 DECLARE 语句”
-
存储过程参数:第 15.1.17 节,“CREATE PROCEDURE 和 CREATE FUNCTION 语句”
param_marker 是用于占位符的预处理语句中使用的 ?。请参见第 15.5.1 节,“PREPARE 语句”。
(*subquery*) 表示返回单个值的子查询;即标量子查询。参见 Section 15.2.15.1, “标量操作数的子查询”。
{*identifier* *expr*} 是 ODBC 转义语法,为了兼容 ODBC 而被接受。值为 expr。语法中的 { 和 } 大括号应该按照字面意义书写;它们不是在其他语法描述中使用的元语法。
match_expr 表示一个 MATCH 表达式。参见 Section 14.9, “全文搜索函数”。
case_expr 表示一个 CASE 表达式。参见 Section 14.5, “流程控制函数”。
interval_expr 表示一个时间间隔。参见 时间间隔。
时间间隔
interval_expr 在表达式中表示一个时间间隔。间隔具有以下语法:
INTERVAL *expr* *unit*
expr 代表一个数量。unit 代表用于解释数量的单位;它是一个类似于 小时, 天 或 周 的指定符号。INTERVAL 关键字和 unit 指定符号不区分大小写。
以下表格显示了每个 unit 值的 expr 参数的预期形式。
表 11.2 时间间隔表达式和单位参数
unit 值 | 预期的 expr 格式 |
|---|---|
MICROSECOND | 微秒 |
SECOND | 秒 |
MINUTE | 分钟 |
HOUR | 小时 |
DAY | 天 |
WEEK | 周 |
MONTH | 月 |
QUARTER | 季度 |
YEAR | 年 |
SECOND_MICROSECOND | '秒.微秒' |
MINUTE_MICROSECOND | '分钟:秒.微秒' |
MINUTE_SECOND | '分钟:秒' |
HOUR_MICROSECOND | '小时:分钟:秒.微秒' |
HOUR_SECOND | '小时:分钟:秒' |
HOUR_MINUTE | '小时:分钟' |
DAY_MICROSECOND | '天 小时:分钟:秒.微秒' |
DAY_SECOND | '天 小时:分钟:秒' |
DAY_MINUTE | '天 小时:分钟' |
DAY_HOUR | '天 小时' |
YEAR_MONTH | '年-月' |
unit 值 | 预期的 expr 格式 |
MySQL 允许在 expr 格式中使用任何标点符号分隔符。表中显示的是建议的分隔符。
时间间隔用于某些函数,例如 DATE_ADD() 和 DATE_SUB():
mysql> SELECT DATE_ADD('2018-05-01',INTERVAL 1 DAY);
-> '2018-05-02'
mysql> SELECT DATE_SUB('2018-05-01',INTERVAL 1 YEAR);
-> '2017-05-01'
mysql> SELECT DATE_ADD('2020-12-31 23:59:59',
-> INTERVAL 1 SECOND);
-> '2021-01-01 00:00:00'
mysql> SELECT DATE_ADD('2018-12-31 23:59:59',
-> INTERVAL 1 DAY);
-> '2019-01-01 23:59:59'
mysql> SELECT DATE_ADD('2100-12-31 23:59:59',
-> INTERVAL '1:1' MINUTE_SECOND);
-> '2101-01-01 00:01:00'
mysql> SELECT DATE_SUB('2025-01-01 00:00:00',
-> INTERVAL '1 1:1:1' DAY_SECOND);
-> '2024-12-30 22:58:59'
mysql> SELECT DATE_ADD('1900-01-01 00:00:00',
-> INTERVAL '-1 10' DAY_HOUR);
-> '1899-12-30 14:00:00'
mysql> SELECT DATE_SUB('1998-01-02', INTERVAL 31 DAY);
-> '1997-12-02'
mysql> SELECT DATE_ADD('1992-12-31 23:59:59.000002',
-> INTERVAL '1.999999' SECOND_MICROSECOND);
-> '1993-01-01 00:00:01.000001'
也可以使用 INTERVAL 与 + 或 - 运算符在表达式中执行时间算术:
date + INTERVAL *expr* *unit*
date - INTERVAL *expr* *unit*
如果另一侧的表达式是日期或日期时间值,则+运算符的两侧都允许INTERVAL *expr* *unit*。对于-运算符,只允许在右侧使用INTERVAL *expr* *unit`*,因为从间隔中减去日期或日期时间值是没有意义的。
mysql> SELECT '2018-12-31 23:59:59' + INTERVAL 1 SECOND;
-> '2019-01-01 00:00:00'
mysql> SELECT INTERVAL 1 DAY + '2018-12-31';
-> '2019-01-01'
mysql> SELECT '2025-01-01' - INTERVAL 1 SECOND;
-> '2024-12-31 23:59:59'
EXTRACT()函数使用与DATE_ADD()或DATE_SUB()相同类型的*unit*指示符,但是从日期中提取部分而不是执行日期算术:
mysql> SELECT EXTRACT(YEAR FROM '2019-07-02');
-> 2019
mysql> SELECT EXTRACT(YEAR_MONTH FROM '2019-07-02 01:02:03');
-> 201907
时间间隔可以在CREATE EVENT语句中使用:
CREATE EVENT myevent
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
DO
UPDATE myschema.mytable SET mycol = mycol + 1;
如果您指定的间隔值太短(不包括从*unit关键字中预期的所有间隔部分),MySQL 会假定您省略了间隔值的最左边部分。例如,如果您指定了DAY_SECOND作为unit,则expr*的值应包含天、小时、分钟和秒部分。如果您指定类似 '1:10' 的值,MySQL 会假定缺少天数和小时数部分,该值表示分钟和秒数。换句话说,'1:10' DAY_SECOND被解释为等同于'1:10' MINUTE_SECOND。这类似于 MySQL 解释TIME值表示经过的时间而不是一天中的时间。
*expr*被视为字符串,因此如果您在INTERVAL中指定非字符串值,请小心。例如,使用HOUR_MINUTE的间隔符号,'6/4'被视为 6 小时 4 分钟,而6/4计算结果为1.5000,被视为 1 小时 5000 分钟:
mysql> SELECT '6/4', 6/4;
-> 1.5000
mysql> SELECT DATE_ADD('2019-01-01', INTERVAL '6/4' HOUR_MINUTE);
-> '2019-01-01 06:04:00'
mysql> SELECT DATE_ADD('2019-01-01', INTERVAL 6/4 HOUR_MINUTE);
-> '2019-01-04 12:20:00'
为了确保间隔值的解释符合您的期望,可以使用CAST()操作。将6/4视为 1 小时 5 分钟,将其转换为具有单个小数位的DECIMAL - DECIMAL, NUMERIC")值:
mysql> SELECT CAST(6/4 AS DECIMAL(3,1));
-> 1.5
mysql> SELECT DATE_ADD('1970-01-01 12:00:00',
-> INTERVAL CAST(6/4 AS DECIMAL(3,1)) HOUR_MINUTE);
-> '1970-01-01 13:05:00'
如果您向日期值添加或减去包含时间部分的内容,则结果会自动转换为日期时间值:
mysql> SELECT DATE_ADD('2023-01-01', INTERVAL 1 DAY);
-> '2023-01-02'
mysql> SELECT DATE_ADD('2023-01-01', INTERVAL 1 HOUR);
-> '2023-01-01 01:00:00'
如果您添加MONTH、YEAR_MONTH或YEAR,并且结果日期的天数大于新月份的最大天数,则天数会调整为新月份的最大天数:
mysql> SELECT DATE_ADD('2019-01-30', INTERVAL 1 MONTH);
-> '2019-02-28'
日期算术运算需要完整的日期,不适用于不完整的日期,如'2016-07-00'或格式错误的日期:
mysql> SELECT DATE_ADD('2016-07-00', INTERVAL 1 DAY);
-> NULL
mysql> SELECT '2005-03-32' + INTERVAL 1 MONTH;
-> NULL
11.6 查询属性
SQL 语句最显著的部分是语句的文本。从 MySQL 8.0.23 开始,客户端还可以定义适用于发送到服务器执行的下一个语句的查询属性:
-
在发送语句之前定义属性。
-
属性存在直到语句执行结束,此时属性集被清除。
-
在属性存在期间,它们可以在服务器端访问。
查询属性可能被用于的示例方式:
-
一个 Web 应用程序生成页面,生成数据库查询,并且对于每个查询必须跟踪生成它的页面的 URL。
-
应用程序在每个查询中传递额外的处理信息,供插件(如审计插件或查询重写插件)使用。
MySQL 支持这些功能,无需使用诸如在查询字符串中包含特殊格式的注释之类的变通方法。本节的其余部分描述了如何使用查询属性支持,包括必须满足的先决条件。
-
定义和访问查询属性
-
使用查询属性的先决条件
-
查询属性可加载函数
定义和访问查询属性
使用 MySQL C API 的应用程序通过调用mysql_bind_param()函数定义查询属性。参见 mysql_bind_param()。其他 MySQL 连接器也可能提供查询属性支持。请参阅各个连接器的文档。
mysql客户端具有query_attributes命令,可以定义最多 32 对属性名称和值。参见 Section 6.5.1.2, “mysql 客户端命令”。
查询属性名称使用由character_set_client系统变量指示的字符集传输。
要在已定义属性的 SQL 语句中访问查询属性,请按照使用查询属性的先决条件中描述的方式安装query_attributes组件。该组件实现了一个mysql_query_attribute_string()可加载函数,该函数接受属性名称参数并将属性值作为字符串返回,如果属性不存在,则返回NULL。参见查询属性可加载函数。
以下示例使用mysql客户端的query_attributes命令来定义属性名称/值对,并使用mysql_query_attribute_string()函数通过名称访问属性值。
此示例定义了两个名为n1和n2的属性。第一个SELECT显示了如何检索这些属性,并演示了检索不存在属性(n3)会返回NULL。第二个SELECT显示了属性在语句之间不会持久存在。
mysql> query_attributes n1 v1 n2 v2;
mysql> SELECT
mysql_query_attribute_string('n1') AS 'attr 1',
mysql_query_attribute_string('n2') AS 'attr 2',
mysql_query_attribute_string('n3') AS 'attr 3';
+--------+--------+--------+
| attr 1 | attr 2 | attr 3 |
+--------+--------+--------+
| v1 | v2 | NULL |
+--------+--------+--------+
mysql> SELECT
mysql_query_attribute_string('n1') AS 'attr 1',
mysql_query_attribute_string('n2') AS 'attr 2';
+--------+--------+
| attr 1 | attr 2 |
+--------+--------+
| NULL | NULL |
+--------+--------+
如第二个SELECT语句所示,在给定语句之前定义的属性仅对该语句可用,并在语句执行后被清除。要在多个语句中使用属性值,请将其分配给变量。以下示例显示了如何执行此操作,并说明了属性值通过变量在后续语句中可用,但不能通过调用mysql_query_attribute_string()来获取:
mysql> query_attributes n1 v1 n2 v2;
mysql> SET
@attr1 = mysql_query_attribute_string('n1'),
@attr2 = mysql_query_attribute_string('n2');
mysql> SELECT
@attr1, mysql_query_attribute_string('n1') AS 'attr 1',
@attr2, mysql_query_attribute_string('n2') AS 'attr 2';
+--------+--------+--------+--------+
| @attr1 | attr 1 | @attr2 | attr 2 |
+--------+--------+--------+--------+
| v1 | NULL | v2 | NULL |
+--------+--------+--------+--------+
属性也可以通过将它们存储在表中以供以后使用:
mysql> CREATE TABLE t1 (c1 CHAR(20), c2 CHAR(20));
mysql> query_attributes n1 v1 n2 v2;
mysql> INSERT INTO t1 (c1, c2) VALUES(
mysql_query_attribute_string('n1'),
mysql_query_attribute_string('n2')
);
mysql> SELECT * FROM t1;
+------+------+
| c1 | c2 |
+------+------+
| v1 | v2 |
+------+------+
查询属性受到这些限制和限制:
-
如果在将语句发送到服务器执行之前发生多个属性定义操作,则最近的定义操作适用并替换了之前操作中定义的属性。
-
如果使用相同名称定义了多个属性,则尝试检索属性值会产生未定义的结果。
-
使用空名称定义的属性无法通过名称检索。
-
属性对使用
PREPARE准备的语句不可用。 -
mysql_query_attribute_string()函数不能在 DDL 语句中使用。 -
属性不会被复制。调用
mysql_query_attribute_string()函数的语句在所有服务器上不会得到相同的值。
使用查询属性的先决条件
要在已定义属性的 SQL 语句中访问查询属性,必须安装 query_attributes 组件。请使用以下语句执行此操作:
INSTALL COMPONENT "file://component_query_attributes";
组件安装是一次性操作,不需要在每次服务器启动时执行。INSTALL COMPONENT 加载组件,并在 mysql.component 系统表中注册它,以使其在后续服务器启动期间加载。
query_attributes 组件访问查询属性以实现 mysql_query_attribute_string() 函数。参见 第 7.5.4 节,“查询属性组件”。
要卸载 query_attributes 组件,请使用以下语句:
UNINSTALL COMPONENT "file://component_query_attributes";
UNINSTALL COMPONENT 卸载组件,并从 mysql.component 系统表中注销它,以使其在后续服务器启动期间不被加载。
因为安装和卸载 query_attributes 组件会安装和卸载组件实现的 mysql_query_attribute_string() 函数,所以不需要使用 CREATE FUNCTION 或 DROP FUNCTION 来执行此操作。
查询属性可加载函数
-
mysql_query_attribute_string(*name*)应用程序可以定义应用于发送到服务器的下一个查询的属性。
mysql_query_attribute_string()函数自 MySQL 8.0.23 起可用,根据属性名称返回属性值作为字符串。此函数使查询能够访问和合并适用于它的属性值。通过安装
query_attributes组件来安装mysql_query_attribute_string()。参见 第 11.6 节,“查询属性”,该节还讨论了查询属性的目的和用途。参数:
name: 属性名称。
返回值:
返回成功的属性值作为字符串,如果属性不存在则返回
NULL。示例:
以下示例使用 mysql 客户端
query_attributes命令来定义可以被mysql_query_attribute_string()检索的查询属性。SELECT显示检索不存在属性(n3)返回NULL。mysql> query_attributes n1 v1 n2 v2; mysql> SELECT -> mysql_query_attribute_string('n1') AS 'attr 1', -> mysql_query_attribute_string('n2') AS 'attr 2', -> mysql_query_attribute_string('n3') AS 'attr 3'; +--------+--------+--------+ | attr 1 | attr 2 | attr 3 | +--------+--------+--------+ | v1 | v2 | NULL | +--------+--------+--------+
11.7 注释
MySQL 服务器支持三种注释样式:
-
从
#字符到行尾。 -
从
--序列到行尾。在 MySQL 中,--(双破折号)注释样式要求第二个破折号后至少跟随一个空格或控制字符(如空格、制表符、换行符等)。这种语法与标准 SQL 注释语法略有不同,如 1.6.2.4 节,“'--'作为注释的开始”中所讨论的。 -
从
/*序列到后续的*/序列,就像在 C 编程语言中一样。这种语法使得注释可以跨越多行,因为开始和结束序列不需要在同一行上。
以下示例演示了所有三种注释样式:
mysql> SELECT 1+1; # This comment continues to the end of line
mysql> SELECT 1+1; -- This comment continues to the end of line
mysql> SELECT 1 /* this is an in-line comment */ + 1;
mysql> SELECT 1+
/*
this is a
multiple-line comment
*/
1;
嵌套注释不受支持,已被弃用;预计在未来的 MySQL 版本中将被移除。(在某些情况下,可能允许嵌套注释,但通常不建议使用。)
MySQL 服务器支持某些 C 风格注释的变体。这使您可以编写包含 MySQL 扩展的代码,但仍然是可移植的,通过使用以下形式的注释:
/*! *MySQL-specific code* */
在这种情况下,MySQL 服务器解析并执行注释中的代码,就像执行任何其他 SQL 语句一样,但其他 SQL 服务器应忽略这些扩展。例如,MySQL 服务器识别以下语句中的STRAIGHT_JOIN关键字,但其他服务器不应该:
SELECT /*! STRAIGHT_JOIN */ col1 FROM table1,table2 WHERE ...
如果在!字符后添加版本号,则仅当 MySQL 版本大于或等于指定版本号时才执行注释内的语法。以下注释中的KEY_BLOCK_SIZE关键字仅由 MySQL 5.1.10 或更高版本的服务器执行:
CREATE TABLE t1(a INT, KEY (a)) /*!50110 KEY_BLOCK_SIZE=1024 */;
版本号使用格式*Mmmrr,其中M是主要版本,mm是两位数的次要版本,rr*是两位数的发布号。例如:在仅由 MySQL 服务器版本 8.0.31 或更高版本运行的语句中,使用80031在注释中。
版本号后应跟至少一个空白字符(或注释的结尾)。从 MySQL 8.0.34 开始,如果不满足此条件,该语句将触发警告;预计这一要求将在未来的 MySQL 版本中得到严格执行。
刚才描述的注释语法适用于mysqld服务器解析 SQL 语句的方式。mysql客户端程序在将语句发送到服务器之前也会对一些语句进行解析。(它这样做是为了确定多语句输入行中的语句边界。)有关服务器和mysql客户端解析器之间的差异的信息,请参见 Section 6.5.1.6, “mysql Client Tips”。
以/*!12345 ... */格式的注释不会存储在服务器上。如果使用此格式对存储的程序进行注释,这些注释不会保留在程序主体中。
另一种 C 风格注释语法的变体用于指定优化器提示。提示注释包括在/*注释开头序列后跟一个+字符。示例:
SELECT /*+ BKA(t1) */ FROM ... ;
更多信息,请参见 Section 10.9.3, “Optimizer Hints”。
在多行/* ... */注释中使用诸如\C之类的mysql短格式命令是不受支持的。短格式命令可以在单行/*! ... */版本注释中工作,/*+ ... */优化器提示注释也可以工作,这些注释存储在对象定义中。如果担心优化器提示注释可能存储在对象定义中,以至于重新加载时使用mysql的转储文件会导致执行此类命令,要么使用--binary-mode选项调用mysql,要么使用除mysql之外的重新加载客户端。
第十二章 字符集、排序规则、Unicode
目录
12.1 一般字符集和排序规则
12.2 MySQL 中的字符集和排序规则
12.2.1 字符集范围
12.2.2 用于元数据的 UTF-8
12.3 指定字符集和排序规则
12.3.1 排序规则命名约定
12.3.2 服务器字符集和排序规则
12.3.3 数据库字符集和排序规则
12.3.4 表的字符集和排序规则
12.3.5 列的字符集和排序规则
12.3.6 字符串文字的字符集和排序规则
12.3.7 国家字符集
12.3.8 字符集引导符
12.3.9 字符集和排序规则分配示例
12.3.10 与其他 DBMS 的兼容性
12.4 连接字符集和排序规则
12.5 配置应用程序字符集和排序规则
12.6 错误消息字符集
12.7 列字符集转换
12.8 排序规则问题
12.8.1 在 SQL 语句中使用 COLLATE
12.8.2 COLLATE 子句优先级
12.8.3 字符集和排序规则的兼容性
12.8.4 表达式中的排序规则强制性
12.8.5 二进制排序规则与 _bin 排序规则的比较
12.8.6 排序规则效果示例
12.8.7 在 INFORMATION_SCHEMA 搜索中使用排序规则
12.9 Unicode 支持
12.9.1 utf8mb4 字符集(4 字节 UTF-8 Unicode 编码)
12.9.2 utf8mb3 字符集(3 字节 UTF-8 Unicode 编码)
12.9.3 utf8 字符集(utf8mb3 的已弃用别名)
12.9.4 ucs2 字符集(UCS-2 Unicode 编码)
12.9.5 utf16 字符集(UTF-16 Unicode 编码)
12.9.6 utf16le 字符集(UTF-16LE Unicode 编码)
12.9.7 utf32 字符集(UTF-32 Unicode 编码)
12.9.8 在 3 字节和 4 字节 Unicode 字符集之间转换
12.10 支持的字符集和排序规则
12.10.1 Unicode 字符集
12.10.2 西欧字符集
12.10.3 中欧字符集
12.10.4 南欧和中东字符集
12.10.5 波罗的海字符集
12.10.6 斯拉夫字符集
12.10.7 亚洲字符集
12.10.8 二进制字符集
12.11 字符集限制
12.12 设置错误消息语言
12.13 添加字符集
12.13.1 字符定义数组
12.13.2 复杂字符集的字符串整理支持
12.13.3 复杂字符集的多字节字符支持
12.14 向字符集添加整理
12.14.1 整理实现类型
12.14.2 选择整理 ID
12.14.3 向 8 位字符集添加简单整理
12.14.4 向 Unicode 字符集添加 UCA 整理
12.15 字符集配置
12.16 MySQL 服务器区域支持
MySQL 包括字符集支持,使您能够使用各种字符集存储数据并根据各种整理进行比较。默认的 MySQL 服务器字符集和整理是utf8mb4和utf8mb4_0900_ai_ci,但您可以在服务器、数据库、表、列和字符串字面量级别指定字符集。
本章讨论以下主题:
-
什么是字符集和整理?
-
字符集分配的多级默认系统。
-
指定字符集和整理的语法。
-
受影响的函数和操作。
-
Unicode 支持。
-
可用的字符集和整理,附注。
-
选择错误消息的语言。
-
选择日期和月份名称的区域设置。
字符集问题不仅影响数据存储,还影响客户端程序与 MySQL 服务器之间的通信。如果希望客户端程序使用与默认字符集不同的字符集与服务器通信,需要指定。例如,要使用utf8mb4 Unicode 字符集,请在连接到服务器后发出以下语句:
SET NAMES 'utf8mb4';
有关为应用程序使用配置字符集和客户端/服务器通信中的字符集相关问题的更多信息,请参阅第 12.5 节,“配置应用程序字符集和整理”,以及第 12.4 节,“连接字符集和整理”。
12.1 一般字符集和排序规则
字符集是一组符号和编码。排序规则是一组比较字符的规则。让我们通过一个想象中的字符集的例子来明确区分。
假设我们有一个有四个字母的字母表:A、B、a、b。我们给每个字母一个数字:A=0,B=1,a=2,b=3。字母A是一个符号,数字 0 是A的编码,所有四个字母及其编码的组合是一个字符集。
假设我们想比较两个字符串值,A和B。最简单的方法是查看编码:A为 0,B为 1。因为 0 小于 1,我们说A小于B。我们刚刚对我们的字符集应用了排序规则。排序规则是一组规则(在这种情况下只有一条规则):“比较编码”。我们称这种可能的排序规则为二进制排序规则。
但是如果我们想要说小写字母和大写字母是等价的呢?那么我们至少需要两条规则:(1) 将小写字母a和b视为等价于A和B;(2) 然后比较编码。我们称之为不区分大小写的排序规则。它比二进制排序规则稍微复杂一些。
在现实生活中,大多数字符集有许多字符:不仅有A和B,还有整个字母表,有时甚至有多个字母表或拥有成千上万个字符的东方文字系统,以及许多特殊符号和标点符号。同样在现实生活中,大多数排序规则有许多规则,不仅用于区分大小写,还用于区分重音(“重音”是附加到字符上的标记,如德语Ö),以及多字符映射(例如在两个德语排序规则中,Ö=OE的规则)。
MySQL 可以为您执行以下操作:
-
使用各种字符集存储字符串。
-
使用各种排序规则比较字符串。
-
在同一服务器、同一数据库甚至同一表中混合不同字符集或排序规则的字符串。
-
允许在任何级别指定字符集和排序规则。
要有效地使用这些功能,您必须了解可用的字符集和排序规则,如何更改默认设置以及它们如何影响字符串运算符和函数的行为。
12.2 MySQL 中的字符集和排序规则
12.2.1 字符集范围
12.2.2 元数据的 UTF-8
MySQL 服务器支持多种字符集,包括几种 Unicode 字符集。要显示可用的字符集,请使用INFORMATION_SCHEMA CHARACTER_SETS表或SHOW CHARACTER SET语句。以下是部分列表。有关更完整的信息,请参见第 12.10 节,“支持的字符集和排序规则”。
mysql> SHOW CHARACTER SET;
+----------+---------------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+---------------------------------+---------------------+--------+
| big5 | Big5 Traditional Chinese | big5_chinese_ci | 2 |
| binary | Binary pseudo charset | binary | 1 |
...
| latin1 | cp1252 West European | latin1_swedish_ci | 1 |
...
| ucs2 | UCS-2 Unicode | ucs2_general_ci | 2 |
...
| utf8mb3 | UTF-8 Unicode | utf8mb3_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 |
...
默认情况下,SHOW CHARACTER SET语句显示所有可用的字符集。它接受一个可选的LIKE或WHERE子句,指示要匹配哪些字符集名称。以下示例显示了一些 Unicode 字符集(基于 Unicode 转换格式)。
mysql> SHOW CHARACTER SET LIKE 'utf%';
+---------+------------------+--------------------+--------+
| Charset | Description | Default collation | Maxlen |
+---------+------------------+--------------------+--------+
| utf16 | UTF-16 Unicode | utf16_general_ci | 4 |
| utf16le | UTF-16LE Unicode | utf16le_general_ci | 4 |
| utf32 | UTF-32 Unicode | utf32_general_ci | 4 |
| utf8mb3 | UTF-8 Unicode | utf8mb3_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 |
+---------+------------------+--------------------+--------+
给定字符集始终至少有一个排序规则,大多数字符集有多个。要列出字符集的显示排序规则,请使用INFORMATION_SCHEMA COLLATIONS表或SHOW COLLATION语句。
默认情况下,SHOW COLLATION语句显示所有可用的排序规则。它接受一个可选的LIKE或WHERE子句,指示要显示哪些排序规则名称。例如,要查看默认字符集utf8mb4的排序规则,请使用以下语句:
mysql> SHOW COLLATION WHERE Charset = '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_bin | utf8mb4 | 46 | | Yes | 1 | PAD SPACE |
| utf8mb4_croatian_ci | utf8mb4 | 245 | | Yes | 8 | PAD SPACE |
| utf8mb4_cs_0900_ai_ci | utf8mb4 | 266 | | Yes | 0 | NO PAD |
| utf8mb4_cs_0900_as_cs | utf8mb4 | 289 | | Yes | 0 | NO PAD |
| utf8mb4_czech_ci | utf8mb4 | 234 | | Yes | 8 | PAD SPACE |
| utf8mb4_danish_ci | utf8mb4 | 235 | | Yes | 8 | PAD SPACE |
| utf8mb4_da_0900_ai_ci | utf8mb4 | 267 | | Yes | 0 | NO PAD |
| utf8mb4_da_0900_as_cs | utf8mb4 | 290 | | Yes | 0 | NO PAD |
| utf8mb4_de_pb_0900_ai_ci | utf8mb4 | 256 | | Yes | 0 | NO PAD |
| utf8mb4_de_pb_0900_as_cs | utf8mb4 | 279 | | Yes | 0 | NO PAD |
| utf8mb4_eo_0900_ai_ci | utf8mb4 | 273 | | Yes | 0 | NO PAD |
| utf8mb4_eo_0900_as_cs | utf8mb4 | 296 | | Yes | 0 | NO PAD |
| utf8mb4_esperanto_ci | utf8mb4 | 241 | | Yes | 8 | PAD SPACE |
| utf8mb4_estonian_ci | utf8mb4 | 230 | | Yes | 8 | PAD SPACE |
| utf8mb4_es_0900_ai_ci | utf8mb4 | 263 | | Yes | 0 | NO PAD |
| utf8mb4_es_0900_as_cs | utf8mb4 | 286 | | Yes | 0 | NO PAD |
| utf8mb4_es_trad_0900_ai_ci | utf8mb4 | 270 | | Yes | 0 | NO PAD |
| utf8mb4_es_trad_0900_as_cs | utf8mb4 | 293 | | Yes | 0 | NO PAD |
| utf8mb4_et_0900_ai_ci | utf8mb4 | 262 | | Yes | 0 | NO PAD |
| utf8mb4_et_0900_as_cs | utf8mb4 | 285 | | Yes | 0 | NO PAD |
| utf8mb4_general_ci | utf8mb4 | 45 | | Yes | 1 | PAD SPACE |
| utf8mb4_german2_ci | utf8mb4 | 244 | | Yes | 8 | PAD SPACE |
| utf8mb4_hr_0900_ai_ci | utf8mb4 | 275 | | Yes | 0 | NO PAD |
| utf8mb4_hr_0900_as_cs | utf8mb4 | 298 | | Yes | 0 | NO PAD |
| utf8mb4_hungarian_ci | utf8mb4 | 242 | | Yes | 8 | PAD SPACE |
| utf8mb4_hu_0900_ai_ci | utf8mb4 | 274 | | Yes | 0 | NO PAD |
| utf8mb4_hu_0900_as_cs | utf8mb4 | 297 | | Yes | 0 | NO PAD |
| utf8mb4_icelandic_ci | utf8mb4 | 225 | | Yes | 8 | PAD SPACE |
| utf8mb4_is_0900_ai_ci | utf8mb4 | 257 | | Yes | 0 | NO PAD |
| utf8mb4_is_0900_as_cs | utf8mb4 | 280 | | Yes | 0 | NO PAD |
| utf8mb4_ja_0900_as_cs | utf8mb4 | 303 | | Yes | 0 | NO PAD |
| utf8mb4_ja_0900_as_cs_ks | utf8mb4 | 304 | | Yes | 24 | NO PAD |
| utf8mb4_latvian_ci | utf8mb4 | 226 | | Yes | 8 | PAD SPACE |
| utf8mb4_la_0900_ai_ci | utf8mb4 | 271 | | Yes | 0 | NO PAD |
| utf8mb4_la_0900_as_cs | utf8mb4 | 294 | | Yes | 0 | NO PAD |
| utf8mb4_lithuanian_ci | utf8mb4 | 236 | | Yes | 8 | PAD SPACE |
| utf8mb4_lt_0900_ai_ci | utf8mb4 | 268 | | Yes | 0 | NO PAD |
| utf8mb4_lt_0900_as_cs | utf8mb4 | 291 | | Yes | 0 | NO PAD |
| utf8mb4_lv_0900_ai_ci | utf8mb4 | 258 | | Yes | 0 | NO PAD |
| utf8mb4_lv_0900_as_cs | utf8mb4 | 281 | | Yes | 0 | NO PAD |
| utf8mb4_persian_ci | utf8mb4 | 240 | | Yes | 8 | PAD SPACE |
| utf8mb4_pl_0900_ai_ci | utf8mb4 | 261 | | Yes | 0 | NO PAD |
| utf8mb4_pl_0900_as_cs | utf8mb4 | 284 | | Yes | 0 | NO PAD |
| utf8mb4_polish_ci | utf8mb4 | 229 | | Yes | 8 | PAD SPACE |
| utf8mb4_romanian_ci | utf8mb4 | 227 | | Yes | 8 | PAD SPACE |
| utf8mb4_roman_ci | utf8mb4 | 239 | | Yes | 8 | PAD SPACE |
| utf8mb4_ro_0900_ai_ci | utf8mb4 | 259 | | Yes | 0 | NO PAD |
| utf8mb4_ro_0900_as_cs | utf8mb4 | 282 | | Yes | 0 | NO PAD |
| utf8mb4_ru_0900_ai_ci | utf8mb4 | 306 | | Yes | 0 | NO PAD |
| utf8mb4_ru_0900_as_cs | utf8mb4 | 307 | | Yes | 0 | NO PAD |
| utf8mb4_sinhala_ci | utf8mb4 | 243 | | Yes | 8 | PAD SPACE |
| utf8mb4_sk_0900_ai_ci | utf8mb4 | 269 | | Yes | 0 | NO PAD |
| utf8mb4_sk_0900_as_cs | utf8mb4 | 292 | | Yes | 0 | NO PAD |
| utf8mb4_slovak_ci | utf8mb4 | 237 | | Yes | 8 | PAD SPACE |
| utf8mb4_slovenian_ci | utf8mb4 | 228 | | Yes | 8 | PAD SPACE |
| utf8mb4_sl_0900_ai_ci | utf8mb4 | 260 | | Yes | 0 | NO PAD |
| utf8mb4_sl_0900_as_cs | utf8mb4 | 283 | | Yes | 0 | NO PAD |
| utf8mb4_spanish2_ci | utf8mb4 | 238 | | Yes | 8 | PAD SPACE |
| utf8mb4_spanish_ci | utf8mb4 | 231 | | Yes | 8 | PAD SPACE |
| utf8mb4_sv_0900_ai_ci | utf8mb4 | 264 | | Yes | 0 | NO PAD |
| utf8mb4_sv_0900_as_cs | utf8mb4 | 287 | | Yes | 0 | NO PAD |
| utf8mb4_swedish_ci | utf8mb4 | 232 | | Yes | 8 | PAD SPACE |
| utf8mb4_tr_0900_ai_ci | utf8mb4 | 265 | | Yes | 0 | NO PAD |
| utf8mb4_tr_0900_as_cs | utf8mb4 | 288 | | Yes | 0 | NO PAD |
| utf8mb4_turkish_ci | utf8mb4 | 233 | | Yes | 8 | PAD SPACE |
| utf8mb4_unicode_520_ci | utf8mb4 | 246 | | Yes | 8 | PAD SPACE |
| utf8mb4_unicode_ci | utf8mb4 | 224 | | Yes | 8 | PAD SPACE |
| utf8mb4_vietnamese_ci | utf8mb4 | 247 | | Yes | 8 | PAD SPACE |
| utf8mb4_vi_0900_ai_ci | utf8mb4 | 277 | | Yes | 0 | NO PAD |
| utf8mb4_vi_0900_as_cs | utf8mb4 | 300 | | Yes | 0 | NO PAD |
| utf8mb4_zh_0900_as_cs | utf8mb4 | 308 | | Yes | 0 | NO PAD |
+----------------------------+---------+-----+---------+----------+---------+---------------+
有关这些排序规则的更多信息,请参见第 12.10.1 节,“Unicode 字符集”。
排序规则具有以下一般特征:
-
两个不同的字符集不能具有相同的排序规则。
-
每个字符集都有一个默认排序。例如,
utf8mb4和latin1的默认排序分别为utf8mb4_0900_ai_ci和latin1_swedish_ci。INFORMATION_SCHEMACHARACTER_SETS表和SHOW CHARACTER SET语句指示每个字符集的默认排序。INFORMATION_SCHEMACOLLATIONS表和SHOW COLLATION语句有一列指示每个排序是否是其字符集的默认排序(如果是,则为Yes,否则为空)。 -
排序名称以它们关联的字符集的名称开头,通常后面跟着一个或多个后缀,表示其他排序特性。有关命名约定的更多信息,请参见第 12.3.1 节,“排序命名约定”。
当一个字符集有多个排序时,可能不清楚哪个排序对于特定应用程序最合适。为了避免选择不合适的排序,对具有代表性数据值的一些比较,以确保给定的排序按照您的预期对值进行排序。
12.2.1 字符集 repertoire
字符集的 repertoire 是集合中的字符。
字符串表达式具有 repertoire 属性,可以有两个值:
-
ASCII:表达式只能包含 ASCII 字符;即 Unicode 范围U+0000到U+007F中的字符。 -
UNICODE:表达式可以包含 Unicode 范围U+0000到U+10FFFF中的字符。这包括基本多文种平面(BMP)范围(U+0000到U+FFFF)中的字符和 BMP 范围之外的补充字符(U+10000到U+10FFFF)中的字符。
ASCII范围是UNICODE范围的子集,因此具有ASCIIrepertoire 的字符串可以安全地转换为具有UNICODErepertoire 的任何字符串的字符集,而不会丢失信息。它也可以安全地转换为任何ascii字符集的超集。(所有 MySQL 字符集都是ascii的超集,除了swe7,它重新使用一些标点字符作为瑞典重音字符。)
使用 repertoire 使得在许多情况下可以进行字符集转换,否则 MySQL 在协定强制性规则无法解决歧义时会返回“collations 混合不合法”错误。(有关强制性的信息,请参见 Section 12.8.4, “Collation Coercibility in Expressions”.)
以下讨论提供了表达式及其 repertoire 的示例,并描述了 repertoire 的使用如何改变字符串表达式的评估:
-
字符串常量的 repertoire 取决于字符串内容,可能与字符串字符集的 repertoire 不同。考虑以下语句:
SET NAMES utf8mb4; SELECT 'abc'; SELECT _utf8mb4'def';尽管在前述每种情况中字符集为
utf8mb4,但实际上字符串并不包含任何 ASCII 范围之外的字符,因此它们的 repertoire 是ASCII而不是UNICODE。 -
具有
ascii字符集的列具有ASCIIrepertoire,因为其字符集。在下表中,c1具有ASCIIrepertoire:CREATE TABLE t1 (c1 CHAR(1) CHARACTER SET ascii);以下示例说明了 repertoire 如何使得在没有 repertoire 的情况下发生错误时能够确定结果:
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;没有 repertoire,会出现以下错误:
ERROR 1267 (HY000): Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (ascii_general_ci,IMPLICIT) for operation 'concat'使用 repertoire,可以发生从子集到超集(
ascii到latin1)的转换,并返回结果:+---------------+ | CONCAT(c1,c2) | +---------------+ | ab | +---------------+ -
具有一个字符串参数的函数继承其参数的 repertoire。
UPPER(_utf8mb4'abc')的结果具有ASCIIrepertoire,因为其参数具有ASCIIrepertoire。(尽管有_utf8mb4引导符,字符串'abc'不包含 ASCII 范围之外的字符。) -
对于返回字符串但不具有字符串参数并使用
character_set_connection作为结果字符集的函数,如果character_set_connection是ascii,则结果字符集是ASCII,否则是UNICODE:FORMAT(*numeric_column*, 4);使用字符集会改变 MySQL 评估以下示例的方式:
SET NAMES ascii; CREATE TABLE t1 (a INT, b VARCHAR(10) CHARACTER SET latin1); INSERT INTO t1 VALUES (1,'b'); SELECT CONCAT(FORMAT(a, 4), b) FROM t1;没有字符集,将出现以下错误:
ERROR 1267 (HY000): Illegal mix of collations (ascii_general_ci,COERCIBLE) and (latin1_swedish_ci,IMPLICIT) for operation 'concat'通过字符集,返回结果:
+-------------------------+ | CONCAT(FORMAT(a, 4), b) | +-------------------------+ | 1.0000b | +-------------------------+ -
具有两个或更多字符串参数的函数使用结果字符集的“最宽”参数字符集,其中
UNICODE比ASCII更宽。考虑以下CONCAT()调用:CONCAT(_ucs2 X'0041', _ucs2 X'0042') CONCAT(_ucs2 X'0041', _ucs2 X'00C2')对于第一个调用,字符集是
ASCII,因为两个参数都在 ASCII 范围内。对于第二个调用,字符集是UNICODE,因为第二个参数超出了 ASCII 范围。 -
函数返回值的字符集是基于仅影响结果字符集和排序的参数的字符集确定的。
IF(column1 < column2, 'smaller', 'greater')结果字符集是
ASCII,因为两个字符串参数(第二个参数和第三个参数)都具有ASCII字符集。第一个参数对结果字符集没有影响,即使表达式使用字符串值。
12.2.2 UTF-8 for Metadata
元数据是“关于数据的数据”。任何描述数据库的东西——而不是数据库的内容——都是元数据。因此,列名、数据库名、用户名称、版本名称以及SHOW的大部分字符串结果都是元数据。这也适用于INFORMATION_SCHEMA中表的内容,因为这些表定义上包含有关数据库对象的信息。
元数据的表示必须满足这些要求:
-
所有元数据必须使用相同的字符集。否则,
SHOW语句或INFORMATION_SCHEMA中表的SELECT语句将无法正常工作,因为这些操作的结果中同一列中的不同行将使用不同的字符集。 -
元数据必须包含所有语言中的所有字符。否则,用户将无法使用自己的语言命名列和表。
为了满足这两个要求,MySQL 将元数据存储在 Unicode 字符集中,即 UTF-8。如果您从不使用带重音或非拉丁字符,这不会造成任何干扰。但如果您使用了,您应该知道元数据是 UTF-8 的。
元数据要求意味着USER()、CURRENT_USER()、SESSION_USER()、SYSTEM_USER()、DATABASE()和VERSION()函数的返回值默认为 UTF-8 字符集。
服务器将character_set_system系统变量设置为元数据字符集的名称:
mysql> SHOW VARIABLES LIKE 'character_set_system';
+----------------------+---------+
| Variable_name | Value |
+----------------------+---------+
| character_set_system | utf8mb3 |
+----------------------+---------+
使用 Unicode 存储元数据不意味着服务器默认以character_set_system字符集返回列标题和DESCRIBE函数的结果。当您使用SELECT column1 FROM t时,服务器将column1名称本身以由character_set_results系统变量的值确定的字符集返回给客户端,该系统变量的默认值为utf8mb4。如果您希望服务器以不同的字符集传递元数据结果,请使用SET NAMES语句强制服务器执行字符集转换。SET NAMES设置character_set_results和其他相关系统变量。(请参阅第 12.4 节,“连接字符集和校对规则”。)或者,客户端程序可以在从服务器接收结果后执行转换。客户端执行转换更有效,但并非所有客户端都能使用此选项。
如果character_set_results设置为NULL,则不执行任何转换,服务器将使用其原始字符集返回元数据(由character_set_system指示的字符集)。
从服务器返回给客户端的错误消息会自动转换为客户端字符集,与元数据一样。
如果您在单个语句中使用(例如)USER()函数进行比较或赋值,不用担心。MySQL 会为您执行一些自动转换。
SELECT * FROM t1 WHERE USER() = latin1_column;
这是因为在比较之前,latin1_column的内容会自动转换为 UTF-8。
INSERT INTO t1 (latin1_column) SELECT USER();
这是因为在赋值之前,USER()的内容会自动转换为latin1。
尽管自动转换不符合 SQL 标准,但标准确实指出每个字符集(在支持的字符方面)都是 Unicode 的“子集”。因为“适用于超集的内容也适用于子集”是一个众所周知的原则,我们认为 Unicode 的排序规则可以用于与非 Unicode 字符串的比较。有关字符串强制转换的更多信息,请参阅第 12.8.4 节,“表达式中的排序规则可强制性”。
12.3 指定字符集和排序规则
12.3.1 排序命名约定
12.3.2 服务器字符集和排序规则
12.3.3 数据库字符集和排序规则
12.3.4 表字符集和排序规则
12.3.5 列字符集和排序规则
12.3.6 字符串文字字符集和排序规则
12.3.7 国家字符集
12.3.8 字符集引导符
12.3.9 字符集和排序规则分配示例
12.3.10 与其他 DBMS 的兼容性
在四个级别上有字符集和排序规则的默认设置:服务器、数据库、表和列。下面各节中的描述可能看起来复杂,但实践中发现,多级默认会导致自然和明显的结果。
CHARACTER SET 用于指定字符集的子句。CHARSET 可以作为 CHARACTER SET 的同义词使用。
字符集问题不仅影响数据存储,还影响客户端程序与 MySQL 服务器之间的通信。如果希望客户端程序使用与默认值不同的字符集与服务器通信,需要指定。例如,要使用 utf8mb4 Unicode 字符集,连接到服务器后发出以下语句:
SET NAMES 'utf8mb4';
欲了解有关客户端/服务器通信中与字符集相关的问题的更多信息,请参阅 第 12.4 节,“连接字符集和排序规则”。
12.3.1 排序命名约定
译文:
dev.mysql.com/doc/refman/8.0/en/charset-collation-names.html
MySQL 排序名称遵循这些约定:
-
排序名称以其关联的字符集名称开头,通常后跟一个或多个指示其他排序特征的后缀。例如,
utf8mb4_0900_ai_ci和latin1_swedish_ci分别是utf8mb4和latin1字符集的排序。binary字符集有一个单一的排序,也称为binary,没有后缀。 -
语言特定的排序包括区域代码或语言名称。例如,
utf8mb4_tr_0900_ai_ci和utf8mb4_hu_0900_ai_ci分别使用土耳其和匈牙利的规则对utf8mb4字符集进行排序。utf8mb4_turkish_ci和utf8mb4_hungarian_ci类似,但基于较早版本的 Unicode Collation Algorithm。 -
排序后缀指示排序是否区分大小写、重音或假名(或它们的某种组合),或者是二进制的。以下表格显示用于指示这些特征的后缀。
Table 12.1 排序后缀含义
后缀 含义 _ai重音不敏感 _as重音敏感 _ci大小写不敏感 _cs大小写敏感 _ks假名敏感 _bin二进制 对于未指定重音敏感性的非二进制排序名称,它由大小写敏感性确定。如果排序名称不包含
_ai或_as,名称中的_ci暗示_ai,名称中的_cs暗示_as。例如,latin1_general_ci明确是大小写不敏感的,隐含是重音不敏感的,latin1_general_cs明确是大小写敏感的,隐含是重音敏感的,utf8mb4_0900_ai_ci明确是大小写不敏感和重音不敏感的。对于日语排序,
_ks后缀表示排序是假名敏感的;也就是说,它区分片假名字符和平假名字符。没有_ks后缀的日语排序不是假名敏感的,对片假名和平假名字符进行排序时视为相等。对于
binary字符集的binary排序,比较基于数字字节值。对于非二进制字符集的_bin排序,比较基于数字字符编码值,对于多字节字符,这些值与字节值不同。有关binary字符集的binary排序和非二进制字符集的_bin排序之间的差异,请参阅 Section 12.8.5, “The binary Collation Compared to _bin Collations”。 -
Unicode 字符集的排序名称可能包含版本号,以指示排序所基于的 Unicode 排序算法(UCA)的版本。在名称中没有版本号的基于 UCA 的排序使用版本 4.0.0 的 UCA 权重键。例如:
-
utf8mb4_0900_ai_ci基于 UCA 9.0.0 权重键(www.unicode.org/Public/UCA/9.0.0/allkeys.txt)。 -
utf8mb4_unicode_520_ci基于 UCA 5.2.0 权重键(www.unicode.org/Public/UCA/5.2.0/allkeys.txt)。 -
utf8mb4_unicode_ci(没有指定版本)基于 UCA 4.0.0 权重键(www.unicode.org/Public/UCA/4.0.0/allkeys-4.0.0.txt)。
-
-
对于 Unicode 字符集,
*xxx*_general_mysql500_ci排序保留了原始*xxx*_general_ci排序的 5.1.24 之前的顺序,并允许升级用于在 MySQL 5.1.24 之前创建的表(Bug #27877)。
12.3.2 服务器字符集和校对
MySQL 服务器有一个服务器字符集和一个服务器校对。默认情况下,它们分别为utf8mb4和utf8mb4_0900_ai_ci,但可以在服务器启动时通过命令行或选项文件显式设置,并在运行时更改。
最初,服务器字符集和校对取决于启动mysqld时使用的选项。您可以使用--character-set-server设置字符集。此外,您还可以添加--collation-server设置校对。如果您没有指定字符集,那就相当于说--character-set-server=utf8mb4。如果您只指定了字符集(例如,utf8mb4)但没有指定校对,那就相当于说--character-set-server=utf8mb4 --collation-server=utf8mb4_0900_ai_ci,因为utf8mb4_0900_ai_ci是utf8mb4的默认校对。因此,以下三个命令都具有相同的效果:
mysqld
mysqld --character-set-server=utf8mb4
mysqld --character-set-server=utf8mb4 \
--collation-server=utf8mb4_0900_ai_ci
更改设置的一种方法是重新编译。在从源代码构建时更改默认服务器字符集和校对,可以使用CMake的DEFAULT_CHARSET和DEFAULT_COLLATION选项。例如:
cmake . -DDEFAULT_CHARSET=latin1
或:
cmake . -DDEFAULT_CHARSET=latin1 \
-DDEFAULT_COLLATION=latin1_german1_ci
mysqld和CMake都会验证字符集/校对组合是否有效。如果无效,每个程序都会显示错误消息并终止。
如果在CREATE DATABASE语句中未指定数据库字符集和校对,服务器字符集和校对将用作默认值。它们没有其他用途。
可以通过character_set_server和collation_server系统变量的值来确定当前服务器字符集和校对。这些变量可以在运行时更改。
12.3.3 数据库字符集和排序规则
每个数据库都有一个数据库字符集和一个数据库排序规则。CREATE DATABASE和ALTER DATABASE语句具有用于指定数据库字符集和排序规则的可选子句:
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*]
关键字SCHEMA可以代替DATABASE。
CHARACTER SET和COLLATE子句使得在同一 MySQL 服务器上创建具有不同字符集和排序规则的数据库成为可能。
数据库选项存储在数据字典中,可以通过检查信息模式SCHEMATA表来查看。
示例:
CREATE DATABASE *db_name* CHARACTER SET latin1 COLLATE latin1_swedish_ci;
MySQL 按以下方式选择数据库字符集和数据库排序规则:
-
如果同时指定了
CHARACTER SET *charset_name*和COLLATE *collation_name*,则使用字符集*charset_name和排序规则collation_name*。 -
如果指定了
CHARACTER SET *charset_name*而没有指定COLLATE,则使用字符集*charset_name*及其默认排序规则。要查看每个字符集的默认排序规则,请使用SHOW CHARACTER SET语句或查询INFORMATION_SCHEMACHARACTER_SETS表。 -
如果指定了
COLLATE *collation_name*而没有指定CHARACTER SET,则使用与*collation_name关联的字符集和排序规则collation_name*。 -
否则(既未指定
CHARACTER SET也未指定COLLATE),则使用服务器字符集和服务器排序规则。
可以从character_set_database和collation_database系统变量的值确定默认数据库的字符集和排序规则。每当默认数据库更改时,服务器会设置这些变量的值。如果没有默认数据库,则这些变量的值与相应的服务器级系统变量character_set_server和collation_server相同。
要查看给定数据库的默认字符集和排序规则,请使用以下语句:
USE *db_name*;
SELECT @@character_set_database, @@collation_database;
或者,要显示值而不更改默认数据库:
SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '*db_name*';
数据库字符集和排序规则影响服务器操作的这些方面:
-
对于
CREATE TABLE语句,如果未指定表的字符集和校对规则,则数据库字符集和校对规则将作为表定义的默认值。要覆盖此设置,请提供明确的CHARACTER SET和COLLATE表选项。 -
对于包含没有
CHARACTER SET子句的LOAD DATA语句,服务器将使用character_set_database系统变量指示的字符集来解释文件中的信息。要覆盖此设置,请提供明确的CHARACTER SET子句。 -
对于存储过程(procedures)和函数(functions),在创建过程中使用的数据库字符集和校对规则将作为字符数据参数的字符集和校对规则,如果声明中未包含
CHARACTER SET或COLLATE属性。要覆盖此设置,请明确提供CHARACTER SET和COLLATE。
12.3.4 表字符集和排序规则
每个表都有一个表字符集和一个表排序规则。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*]
示例:
CREATE TABLE t1 ( ... )
CHARACTER SET latin1 COLLATE latin1_danish_ci;
MySQL 选择表字符集和排序规则的方式如下:
-
如果同时指定了
CHARACTER SET *charset_name*和COLLATE *collation_name*,则使用字符集*charset_name和排序规则collation_name*。 -
如果指定了
CHARACTER SET *charset_name*,但没有指定COLLATE,则使用字符集*charset_name*及其默认排序规则。要查看每个字符集的默认排序规则,请使用SHOW CHARACTER SET语句或查询INFORMATION_SCHEMACHARACTER_SETS表。 -
如果指定了
COLLATE *collation_name*,但没有指定CHARACTER SET,则使用与*collation_name相关联的字符集和排序规则collation_name*。 -
否则(未指定
CHARACTER SET或COLLATE),则使用数据库字符集和排序规则。
如果在单独的列定义中未指定列字符集和排序规则,则表字符集和排序规则将用作列定义的默认值。表字符集和排序规则是 MySQL 的扩展;标准 SQL 中没有这样的东西。
12.3.5 列字符集和排序规则
每个“字符”列(即CHAR、VARCHAR类型的列,TEXT类型,或任何同义词)都有一个列字符集和一个列排序规则。用于CREATE TABLE和ALTER TABLE的列定义语法具有可选子句,用于指定列字符集和排序规则:
*col_name* {CHAR | VARCHAR | TEXT} (*col_length*)
[CHARACTER SET *charset_name*]
[COLLATE *collation_name*]
这些子句也可用于ENUM和SET列:
*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 选择列的字符集和排序规则的方式如下:
-
如果同时指定了
CHARACTER SET *charset_name*和COLLATE *collation_name*,则使用字符集*charset_name和排序规则collation_name*。CREATE TABLE t1 ( col1 CHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ) CHARACTER SET latin1 COLLATE latin1_bin;该列已指定字符集和排序规则,因此使用它们。该列的字符集为
utf8mb4,排序规则为utf8mb4_unicode_ci。 -
如果指定了
CHARACTER SET *charset_name*但未指定COLLATE,则使用字符集*charset_name*及其默认排序规则。CREATE TABLE t1 ( col1 CHAR(10) CHARACTER SET utf8mb4 ) CHARACTER SET latin1 COLLATE latin1_bin;对于该列,指定了字符集,但未指定排序规则。该列的字符集为
utf8mb4,默认排序规则为utf8mb4_0900_ai_ci。要查看每个字符集的默认排序规则,请使用SHOW CHARACTER SET语句或查询INFORMATION_SCHEMACHARACTER_SETS表。 -
如果指定了
COLLATE *collation_name*但未指定CHARACTER SET,则使用与*collation_name相关联的字符集和排序规则collation_name*。CREATE TABLE t1 ( col1 CHAR(10) COLLATE utf8mb4_polish_ci ) CHARACTER SET latin1 COLLATE latin1_bin;对于该列,指定了排序规则,但未指定字符集。该列的排序规则为
utf8mb4_polish_ci,字符集为与排序规则相关联的字符集,即utf8mb4。 -
否则(既未指定
CHARACTER SET也未指定COLLATE),则使用表的字符集和排序规则。CREATE TABLE t1 ( col1 CHAR(10) ) CHARACTER SET latin1 COLLATE latin1_bin;对于该列,既未指定字符集也未指定排序规则,因此使用表的默认设置。该列的字符集为
latin1,排序规则为latin1_bin。
CHARACTER SET和COLLATE子句是标准 SQL。
如果使用ALTER TABLE将列从一个字符集转换为另一个字符集,MySQL 会尝试映射数据值,但如果字符集不兼容,则可能会丢失数据。
12.3.6 字符字符串字面量的字符集和校对规则
每个字符字符串字面量都有一个字符集和一个校对规则。
对于简单语句SELECT '*string*',该字符串具有由character_set_connection和collation_connection系统变量定义的连接默认字符集和���对规则。
字符字符串字面量可以具有可选的字符集引导符和COLLATE子句,以指定其为使用特定字符集和校对规则的字符串:
[_*charset_name*]'*string*' [COLLATE *collation_name*]
_*charset_name*表达式在形式上称为引导符。它告诉解析器,“接下来的字符串使用字符集*charset_name*。”引导符不会像CONVERT()那样将字符串转换为引导符字符集。它不会改变字符串值,尽管可能会发生填充。引导符只是一个信号。参见第 12.3.8 节,“字符集引导符”。
例子:
SELECT 'abc';
SELECT _latin1'abc';
SELECT _binary'abc';
SELECT _utf8mb4'abc' COLLATE utf8mb4_danish_ci;
字符集引导符和COLLATE子句按照标准 SQL 规范实现。
MySQL 确定字符字符串字面量的字符集和校对规则的方式如下:
-
如果同时指定了*
_charset_name和COLLATE *collation_name*,则使用字符集charset_name和校对规则collation_name。collation_name必须是charset_name*的允许校对规则。 -
如果*
_charset_name被指定但未指定COLLATE,则使用字符集charset_name*及其默认校对规则。要查看每个字符集的默认校对规则,请使用SHOW CHARACTER SET语句或查询INFORMATION_SCHEMACHARACTER_SETS表。 -
如果未指定*
_charset_name但指定了COLLATE *collation_name*,则使用由character_set_connection系统变量给出的连接默认字符集和校对规则collation_name。collation_name*必须是连接默认字符集的允许校对规则。 -
否则(既未指定*
_charset_name*也未指定COLLATE *collation_name*),则使用由character_set_connection和collation_connection系统变量给出的连接默认字符集和校对规则。
例子:
-
一个使用
latin1字符集和latin1_german1_ci校对规则的非二进制字符串:SELECT _latin1'Müller' COLLATE latin1_german1_ci; -
一个带有
utf8mb4字符集和其默认排序规则(即utf8mb4_0900_ai_ci)的非二进制字符串:SELECT _utf8mb4'Müller'; -
一个带有
binary字符集和其默认排序规则(即binary)的二进制字符串:SELECT _binary'Müller'; -
一个带有连接默认字符集和
utf8mb4_0900_ai_ci排序规则的非二进制字符串(如果连接字符集不是utf8mb4,则失败):SELECT 'Müller' COLLATE utf8mb4_0900_ai_ci; -
一个带有连接默认字符集和排序规则的字符串:
SELECT 'Müller';
一个引导符指示了接下来字符串的字符集,但不会改变解析器在字符串内部执行转义处理的方式。转义始终由解析器根据character_set_connection给定的字符集来解释。
以下示例显示,即使有引导符存在,转义处理仍然使用character_set_connection。这些示例使用SET NAMES(更改character_set_connection的内容,如第 12.4 节“连接字符集和排序规则”中所讨论的),并使用HEX()函数显示结果字符串,以便查看确切的字符串内容。
示例 1:
mysql> SET NAMES latin1;
mysql> SELECT HEX('à\n'), HEX(_sjis'à\n');
+------------+-----------------+
| HEX('à\n') | HEX(_sjis'à\n') |
+------------+-----------------+
| E00A | E00A |
+------------+-----------------+
这里,à(十六进制值E0)后面跟着\n,这是换行的转义序列。转义序列使用latin1的character_set_connection值来产生一个字面换行(十六进制值0A)。即使对于第二个字符串也是如此。也就是说,_sjis引导符不会影响解析器的转义处理。
示例 2:
mysql> SET NAMES sjis;
mysql> SELECT HEX('à\n'), HEX(_latin1'à\n');
+------------+-------------------+
| HEX('à\n') | HEX(_latin1'à\n') |
+------------+-------------------+
| E05C6E | E05C6E |
+------------+-------------------+
这里,character_set_connection是sjis,一个字符集,其中à后面跟着\(十六进制值05和5C)是一个有效的多字节字符。因此,字符串的前两个字节被解释为一个单个的sjis字符,\不被解释为转义字符。接下来的n(十六进制值6E)不被解释为转义序列的一部分。即使对于第二个字符串也是如此;_latin1引导符不会影响转义处理。
12.3.7 国家字符集
标准 SQL 将NCHAR或NATIONAL CHAR定义为指示CHAR列应使用某个预定义字符集的方法。MySQL 使用utf8作为这个预定义字符集。例如,以下数据类型声明是等效的:
CHAR(10) CHARACTER SET utf8
NATIONAL CHARACTER(10)
NCHAR(10)
此外还有这些:
VARCHAR(10) CHARACTER SET utf8
NATIONAL VARCHAR(10)
NVARCHAR(10)
NCHAR VARCHAR(10)
NATIONAL CHARACTER VARYING(10)
NATIONAL CHAR VARYING(10)
您可以使用N'*literal*'(或n'*literal*')来创建一个国家字符集中的字符串。以下语句是等效的:
SELECT N'some text';
SELECT n'some text';
SELECT _utf8'some text';
MySQL 8.0 将国家字符集解释为utf8mb3,这种方式现在已经被弃用。因此,使用NATIONAL CHARACTER或其同义词之一来定义数据库、表或列的字符集会引发类似于以下警告:
NATIONAL/NCHAR/NVARCHAR implies the character set UTF8MB3, which will be
replaced by UTF8MB4 in a future release. Please consider using CHAR(x) CHARACTER
SET UTF8MB4 in order to be unambiguous.
12.3.8 字符集引导符
字符串文字、十六进制文字或位值文字可以有可选的字符集引导符和COLLATE子句,以指定它为使用特定字符集和校对规则的字符串:
[_*charset_name*] *literal* [COLLATE *collation_name*]
_*charset_name*表达式在形式上称为引导符。它告诉解析器,“接下来的字符串使用字符集*charset_name*。” 引导符不会像CONVERT()那样将字符串更改为引导符字符集。它不会更改字符串值,尽管可能会发生填充。引导符只是一个信号。
对于字符串文字,引导符和字符串之间的空格是允许但是可选的。
对于字符集文字,引导符表示后续字符串的字符集,但不会改变解析器如何在字符串内执行转义处理。转义始终由解析器根据character_set_connection给定的字符集进行解释。有关更多讨论和示例,请参见第 12.3.6 节,“字符串文字的字符集和校对规则”。
例子:
SELECT 'abc';
SELECT _latin1'abc';
SELECT _binary'abc';
SELECT _utf8mb4'abc' COLLATE utf8mb4_danish_ci;
SELECT _latin1 X'4D7953514C';
SELECT _utf8mb4 0x4D7953514C COLLATE utf8mb4_danish_ci;
SELECT _latin1 b'1000001';
SELECT _utf8mb4 0b1000001 COLLATE utf8mb4_danish_ci;
字符集引导符和COLLATE子句按照标准 SQL 规范实现。
字符串文字可以通过使用_binary引导符指定为二进制字符串。十六进制文字和位值文字默认为二进制字符串,因此允许使用_binary,但通常是不必要的。在 MySQL 8.0 及更高版本中,位操作允许数字或二进制字符串参数,但默认情况下将十六进制和位文字视为数字。为了明确指定这些文字的二进制字符串上下文,对于这些文字至少一个参数使用_binary引导符:
mysql> SET @v1 = X'000D' | X'0BC0';
mysql> SET @v2 = _binary X'000D' | X'0BC0';
mysql> SELECT HEX(@v1), HEX(@v2);
+----------+----------+
| HEX(@v1) | HEX(@v2) |
+----------+----------+
| BCD | 0BCD |
+----------+----------+
对于位操作,显示的结果看起来相似,但没有_binary的结果是一个BIGINT值,而有_binary的结果是一个二进制字符串。由于结果类型的差异,显示的值也不同:高位 0 数字不会显示在数值结果中。
MySQL 确定字符串文字、十六进制文字或位值文字的字符集和校对规则的方式如下:
-
如果同时指定了*
_charset_name和COLLATE *collation_name*,则使用字符集charset_name和校对规则collation_name。collation_name必须是charset_name*的允许校对规则。 -
如果指定了*
_charset_name但未指定COLLATE,则使用字符集charset_name*及其默认排序规则。要查看每个字符集的默认排序规则,请使用SHOW CHARACTER SET语句或查询INFORMATION_SCHEMACHARACTER_SETS表。 -
如果未指定*
_charset_name*但指定了COLLATE *collation_name*:-
对于字符字符串字面值,使用由
character_set_connection系统变量给出的连接默认字符集和排序规则*collation_name。collation_name*必须是连接默认字符集的允许排序规则。 -
对于十六进制字面值或位值字面值,唯一允许的排序规则是
binary,因为这些类型的字面值默认为二进制字符串。
-
-
否则(既未指定*
_charset_name*也未指定COLLATE *collation_name*):-
对于字符字符串字面值,使用由
character_set_connection和collation_connection系统变量给出的连接默认字符集和排序规则。 -
对于十六进制字面值或位值字面值,字符集和排序规则为
binary。
-
例子:
-
具有
latin1字符集和latin1_german1_ci排序规则的非二进制字符串:SELECT _latin1'Müller' COLLATE latin1_german1_ci; SELECT _latin1 X'0A0D' COLLATE latin1_german1_ci; SELECT _latin1 b'0110' COLLATE latin1_german1_ci; -
具有
utf8mb4字符集及其默认排序规则(即utf8mb4_0900_ai_ci)的非二进制字符串:SELECT _utf8mb4'Müller'; SELECT _utf8mb4 X'0A0D'; SELECT _utf8mb4 b'0110'; -
具有
binary字符集及其默认排序规则(即binary)的二进制字符串:SELECT _binary'Müller'; SELECT X'0A0D'; SELECT b'0110';十六进制字面值和位值字面值不需要引导符,因为它们默认为二进制字符串。
-
一个具有连接默认字符集和
utf8mb4_0900_ai_ci排序规则的非二进制字符串(如果连接字符集不是utf8mb4,则失败):SELECT 'Müller' COLLATE utf8mb4_0900_ai_ci;这个构造(
COLLATEonly)对十六进制字面值或位字面值不起作用,因为它们的字符集是binary,无论连接字符集如何,而binary与utf8mb4_0900_ai_ci排序规则不兼容。在没有引导符的情况下,唯一允许的COLLATE子句是COLLATE binary。 -
一个具有连接默认字符集和排序规则的字符串:
SELECT 'Müller';
12.3.9 字符集和排序规则分配示例
以下示例展示了 MySQL 如何确定默认字符集和排序规则值。
示例 1:表和列定义
CREATE TABLE t1
(
c1 CHAR(10) CHARACTER SET latin1 COLLATE latin1_german1_ci
) DEFAULT CHARACTER SET latin2 COLLATE latin2_bin;
这里有一个具有latin1字符集和latin1_german1_ci排序规则的列。定义是明确的,因此很直接。请注意,在latin2表中存储latin1列没有问题。
示例 2:表和列定义
CREATE TABLE t1
(
c1 CHAR(10) CHARACTER SET latin1
) DEFAULT CHARACTER SET latin1 COLLATE latin1_danish_ci;
这次我们有一个具有latin1字符集和默认排序规则的列。虽然看起来很自然,但默认排序规则并不是从表级别获取的。相反,因为latin1的默认排序规则始终是latin1_swedish_ci,列c1的排序规则是latin1_swedish_ci(而不是latin1_danish_ci)。
示例 3:表和列定义
CREATE TABLE t1
(
c1 CHAR(10)
) DEFAULT CHARACTER SET latin1 COLLATE latin1_danish_ci;
我们有一个具有默认字符集和默认排序规则的列。在这种情况下,MySQL 会检查表级别以确定列字符集和排序规则。因此,列c1的字符集是latin1,排序规则是latin1_danish_ci。
示例 4:数据库、表和列定义
CREATE DATABASE d1
DEFAULT CHARACTER SET latin2 COLLATE latin2_czech_cs;
USE d1;
CREATE TABLE t1
(
c1 CHAR(10)
);
创建一个没有指定字符集和排序规则的列。我们也没有在表级别指定字符集和排序规则。在这种情况下,MySQL 会检查数据库级别以确定表设置,然后成为列设置。因此,列c1的字符集是latin2,排序规则是latin2_czech_cs。
12.3.10 与其他 DBMS 的兼容性
对于 MaxDB 兼容性,这两个语句是相同的:
CREATE TABLE t1 (f1 CHAR(*N*) UNICODE);
CREATE TABLE t1 (f1 CHAR(*N*) CHARACTER SET ucs2);
UNICODE属性和ucs2字符集在 MySQL 8.0.28 中已被弃用。
12.4 连接字符集和排序规则
“连接”是客户端程序连接到服务器时建立的会话,通过该会话与服务器进行交互。客户端通过会话连接发送 SQL 语句,如查询。服务器通过连接向客户端发送响应,如结果集或错误消息。
-
连接字符集和排序规则系统变量
-
不允许的客户端字符集
-
客户端程序连接字符集配置
-
连接字符集配置的 SQL 语句
-
连接字符集错误处理
连接字符集和排序规则系统变量
几个字符集和排序规则系统变量与客户端与服务器的交互相关。其中一些在前面的章节中已经提到:
-
character_set_server和collation_server系统变量指示服务器的字符集和排序规则。参见 第 12.3.2 节,“服务器字符集和排序规则”。 -
character_set_database和collation_database系统变量指示默认数据库的字符集和排序规则。参见 第 12.3.3 节,“数据库字符集和排序规则”。
处理客户端与服务器之间连接流量涉及到其他字符集和排序规则系统变量。每个客户端都有特定于会话的连接相关字符集和排序规则系统变量。这些会话系统变量值在连接时初始化,但可以在会话中更改。
关于客户端连接的字符集和排序规则处理的几个问题可以通过系统变量来回答:
-
当语句离开客户端时,它们处于什么字符集?
服务器将
character_set_client系统变量视为客户端发送的语句的字符集。注意
一些字符集不能用作客户端字符集。请参阅不允许的客户端字符集。
-
服务器在接收语句后应将其转换为哪种字符集?
为了确定这一点,服务器使用
character_set_connection和collation_connection系统变量:-
服务器将客户端发送的语句从
character_set_client转换为character_set_connection。例外:对于具有诸如_utf8mb4或_latin2之类的引导符的字符串文字,引导符确定字符集。请参阅第 12.3.8 节,“字符集引导符”。 -
collation_connection对于文字字符串的比较很重要。对于与列值进行比较的字符串,collation_connection并不重要,因为列有自己的排序规则,其具有更高的排序规则优先级(参见第 12.8.4 节,“表达式中的排序规则可强制性”)。
-
-
服务器在将查询结果返回给客户端之前应将其转换为哪种字符集?
character_set_results系统变量指示服务器将查询结果返回给客户端的字符集。这包括结果数据,如列值,结果元数据,如列名,以及错误消息。要告诉服务器不要对结果集或错误消息进行任何转换,请将
character_set_results设置为NULL或binary:SET character_set_results = NULL; SET character_set_results = binary;有关字符集和错误消息的更多信息,请参阅第 12.6 节,“错误消息字符集”。
要查看适用于当前会话的字符集和排序规则系统变量的值,请使用此语句:
SELECT * FROM performance_schema.session_variables
WHERE VARIABLE_NAME IN (
'character_set_client', 'character_set_connection',
'character_set_results', 'collation_connection'
) ORDER BY VARIABLE_NAME;
以下更简单的语句也显示连接变量,但同时包括其他相关变量。它们可以用于查看所有字符集和排序规则系统变量:
SHOW SESSION VARIABLES LIKE 'character\_set\_%';
SHOW SESSION VARIABLES LIKE 'collation\_%';
客户端可以微调这些变量的设置,或依赖默认值(在这种情况下,您可以跳过本节的其余部分)。 如果不使用默认值,则必须为每个连接到服务器的连接更改字符设置。
不允许的客户端字符集
character_set_client 系统变量不能设置为某些字符集:
ucs2
utf16
utf16le
utf32
尝试将任何这些字符集用作客户端字符集会产生错误:
mysql> SET character_set_client = 'ucs2';
ERROR 1231 (42000): Variable 'character_set_client'
can't be set to the value of 'ucs2'
如果在以下情况下使用任何这些字符集,都会发生相同的错误,所有这些情况都会尝试将character_set_client设置为指定的字符集:
-
MySQL 客户端程序(如mysql和mysqladmin)使用的
--default-character-set=*charset_name*命令选项。 -
SET NAMES '*charset_name*'语句。 -
SET CHARACTER SET '*charset_name*'语句。
客户端程序连接字符集配置
当客户端连接到服务器时,它指示要用于与服务器通信的字符集。 (实际上,客户端指示该字符集的默认排序规则,从中服务器可以确定字符集。)服务器使用此信息将character_set_client、character_set_results、character_set_connection 系统变量设置为字符集,并将collation_connection 设置为字符集的默认排序规则。 实际上,服务器执行了一个等效的SET NAMES 操作。
如果服务器不支持请求的字符集或排序规则,它将回退到使用服务器字符集和排序规则来配置连接。有关此回退行为的更多详细信息,请参阅连接字符集错误处理。
mysql, mysqladmin, mysqlcheck, mysqlimport, 和 mysqlshow 客户端程序确定要使用的默认字符集如下:
-
在没有其他信息的情况下,每个客户端使用编译时的默认字符集,通常为
utf8mb4。 -
每个客户端可以根据操作系统设置自动检测要使用的字符集,例如在 Unix 系统上的
LANG或LC_ALL区域设置环境变量的值,或 Windows 系统上的代码页设置。对于从操作系统获取区域设置的系统,客户端使用它来设置默认字符集,而不是使用编译时的默认值。例如,将LANG设置为ru_RU.KOI8-R会导致使用koi8r字符集。因此,用户可以配置其环境中的区域设置供 MySQL 客户端使用。如果没有完全匹配,操作系统字符集将映射到最接近的 MySQL 字符集。如果客户端不支持匹配的字符集,则使用编译时的默认值。例如,
utf8和utf-8映射到utf8mb4,而ucs2不支持作为连接字符集,因此映射到编译时的默认值。C 应用程序可以根据操作系统设置使用字符集自动检测,方法是在连接到服务器之前调用
mysql_options()如下:mysql_options(mysql, MYSQL_SET_CHARSET_NAME, MYSQL_AUTODETECT_CHARSET_NAME); -
每个客户端支持
--default-character-set选项,允许用户显式指定字符集以覆盖客户端否则确定的默认值。注意
有些字符集不能用作客户端字符集。尝试使用它们与
--default-character-set会产生错误。参见不允许的客户端字符集。
使用mysql客户端,如果要使用与默认字符集不同的字符集,您可以在每次连接到服务器时显式执行一个SET NAMES语句(参见客户端程序连接字符集配置)。为了更轻松地实现相同的结果,可以在选项文件中指定字符集。例如,以下选项文件设置在每次调用mysql时将三个与连接相关的字符集系统变量设置为koi8r:
[mysql]
default-character-set=koi8r
如果您正在使用启用了自动重新连接的mysql客户端(不建议),最好使用charset命令而不是SET NAMES。例如:
mysql> charset koi8r
Charset changed
charset命令发出一个SET NAMES语句,并在连接断开后重新连接时更改mysql使用的默认字符集。
在配置客户端程序时,还必须考虑它们执行的环境。参见第 12.5 节,“配置应用程序字符集和排序规则”。
用于连接字符集配置的 SQL 语句
建立连接后,客户端可以为当前会话更改字符集和排序规则系统变量。这些变量可以使用SET语句单独更改,但另外两个更方便的语句会作为一组影响与连接相关的字符集系统变量:
-
SET NAMES '*charset_name*' [COLLATE '*collation_name*']SET NAMES指示客户端用于向服务器发送 SQL 语句的字符集。因此,SET NAMES 'cp1251'告诉服务器,“来自此客户端的未来传入消息使用字符集cp1251。”它还指定服务器在将结果发送回客户端时应使用的字符集。(例如,如果使用产生结果集的SELECT语句,则指定用于列值的字符集。)一个
SET NAMES '*charset_name*'语句等同于这三个语句:SET character_set_client = *charset_name*; SET character_set_results = *charset_name*; SET character_set_connection = *charset_name*;将
character_set_connection设置为*charset_name也会隐式地将collation_connection设置为charset_name*的默认排序规则。不需要显式设置该排序规则。要指定用于collation_connection的特定排序规则,请添加COLLATE子句:SET NAMES '*charset_name*' COLLATE '*collation_name*' -
SET CHARACTER SET '*charset_name*'SET CHARACTER SET类似于SET NAMES,但将character_set_connection和collation_connection设置为character_set_database和collation_database(如前所述,指示默认数据库的字符集和排序规则)。SET CHARACTER SET *charset_name*语句等同于这三个语句:SET character_set_client = *charset_name*; SET character_set_results = *charset_name*; SET collation_connection = @@collation_database;设置
collation_connection也会隐式地将character_set_connection设置为与排序规则相关联的字符集(等同于执行SET character_set_connection = @@character_set_database)。不需要显式设置character_set_connection。
注意
有些字符集不能作为客户端字符集使用。尝试在SET NAMES或SET CHARACTER SET中使用它们会产生错误。请参阅不允许的客户端字符集。
例如:假设column1定义为CHAR(5) CHARACTER SET latin2。如果不在发出SELECT column1 FROM t之前说SET NAMES或SET CHARACTER SET,那么服务器会使用客户端连接时指定的字符集发送column1的所有值。另一方面,如果在发出SELECT语句之前说SET NAMES 'latin1'或SET CHARACTER SET 'latin1',服务器会在发送结果之前将latin2值转换为latin1。对于不在两个字符集中的字符,转换可能会有损失。
连接字符集错误处理
尝试使用不合适的连接字符集或排序规则可能会产生错误,或导致服务器回退到给定连接的默认字符集和排序规则。本节描述了在配置连接字符集时可能出现的问题。这些问题可能在建立连接时或在已建立连接中更改字符集时发生。
-
连接时错误处理
-
运行时错误处理
连接时错误处理
一些字符集不能用作客户端字符集;参见不允许的客户端字符集。如果指定了一个有效但不允许作为客户端字符集的字符集,服务器会返回一个错误:
$> mysql --default-character-set=ucs2
ERROR 1231 (42000): Variable 'character_set_client' can't be set to
the value of 'ucs2'
如果指定了客户端不认识的字符集,会产生错误:
$> mysql --default-character-set=bogus
mysql: Character set 'bogus' is not a compiled character set and is
not specified in the '/usr/local/mysql/share/charsets/Index.xml' file
ERROR 2019 (HY000): Can't initialize character set bogus
(path: /usr/local/mysql/share/charsets/)
如果指定了客户端识别但服务器不识别的字符集,服务器会回退到其默认字符集和排序规则。假设服务器配置为使用latin1和latin1_swedish_ci作为默认值,并且不认识gb18030作为有效字符集。指定--default-character-set=gb18030的客户端可以连接到服务器,但结果的字符集不是客户端想要的:
mysql> SHOW SESSION VARIABLES LIKE 'character\_set\_%';
+--------------------------+--------+
| Variable_name | Value |
+--------------------------+--------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
...
| character_set_results | latin1 |
...
+--------------------------+--------+
mysql> SHOW SESSION VARIABLES LIKE 'collation_connection';
+----------------------+-------------------+
| Variable_name | Value |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
+----------------------+-------------------+
您可以看到连接系统变量已设置为反映latin1和latin1_swedish_ci的字符集和排序规则。这是因为服务器无法满足客户端字符集请求并回退到其默认值。
在这种情况下,客户端无法使用它想要的字符集,因为服务器不支持。客户端必须要么愿意使用不同的字符集,要么连接到支持所需字符集的不同服务器。
在更微妙的情况下也会出现相同的问题:当客户端告诉服务器使用服务器识别的字符集,但客户端端默认排序规则在服务器端是未知的。例如,当一个 MySQL 8.0 客户端想要使用utf8mb4作为客户端字符集连接到 MySQL 5.7 服务器时,就会出现这种情况。指定--default-character-set=utf8mb4的客户端可以连接到服务器。然而,与前面的例子一样,服务器会回退到其默认字符集和排序规则,而不是客户端请求的内容:
mysql> SHOW SESSION VARIABLES LIKE 'character\_set\_%';
+--------------------------+--------+
| Variable_name | Value |
+--------------------------+--------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
...
| character_set_results | latin1 |
...
+--------------------------+--------+
mysql> SHOW SESSION VARIABLES LIKE 'collation_connection';
+----------------------+-------------------+
| Variable_name | Value |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
+----------------------+-------------------+
为什么会发生这种情况?毕竟,utf8mb4 是 8.0 客户端和 5.7 服务器都知道的,所以它们都识别它。要理解这种行为,有必要了解当客户端告诉服务器它想要使用哪种字符集时,实际上是告诉服务器该字符集的默认排序规则。因此,上述行为是由多种因素的组合造成的:
-
utf8mb4的默认排序规则在 MySQL 5.7 和 8.0 之间不同(5.7 为utf8mb4_general_ci,8.0 为utf8mb4_0900_ai_ci)。 -
当 8.0 客户端请求一个字符集为
utf8mb4时,发送给服务器的是默认的 8.0utf8mb4排序规则;即utf8mb4_0900_ai_ci。 -
utf8mb4_0900_ai_ci仅在 MySQL 8.0 中实现,因此 5.7 服务器不识别它。 -
由于 5.7 服务器不识别
utf8mb4_0900_ai_ci,无法满足客户端字符集请求,因此回退到其默认字符集和排序规则(latin1和latin1_swedish_ci)。
在这种情况下,客户端仍然可以在连接后发出 SET NAMES 'utf8mb4' 语句来使用 utf8mb4。结果的排序规则是 5.7 默认的 utf8mb4 排序规则;即 utf8mb4_general_ci。如果客户端还想要 utf8mb4_0900_ai_ci 的排序规则,由于服务器不识别该排序规则,无法实现。客户端必须要么愿意使用不同的 utf8mb4 排序规则,要么连接到 MySQL 8.0 或更高版本的服务器。
运行时错误处理
在已建立的连接中,客户端可以通过 SET NAMES 或 SET CHARACTER SET 请求更改连接字符集和排序规则。
一些字符集不能用作客户端字符集;请参阅不允许的客户端字符集。如果指定了一个有效但不允许作为客户端字符集的字符集,服务器会返回一个错误:
mysql> SET NAMES 'ucs2';
ERROR 1231 (42000): Variable 'character_set_client' can't be set to
the value of 'ucs2'
如果服务器不识别字符集(或排序规则),则会产生错误:
mysql> SET NAMES 'bogus';
ERROR 1115 (42000): Unknown character set: 'bogus'
mysql> SET NAMES 'utf8mb4' COLLATE 'bogus';
ERROR 1273 (HY000): Unknown collation: 'bogus'
提示
一个想要验证服务器是否接受其请求的字符集的客户端可以在连接后执行以下语句,并检查结果是否是预期的字符集:
SELECT @@character_set_client;