MySQL8 中文参考(五十五)
14.24 精度数学
14.24.1 数值类型
14.24.2 DECIMAL 数据类型特性
14.24.3 表达式处理
14.24.4 四舍五入行为
14.24.5 精度数学示例
MySQL 提供了对精度数学的支持:处理数字值以获得极其准确的结果,并对无效值具有高度控制。精度数学基于这两个特性:
-
控制服务器对接受或拒绝无效数据的严格程度的 SQL 模式。
-
MySQL 固定点算术库。
这些特性对数字操作有几个影响,并提供了与标准 SQL 高度一致性:
-
精确计算:对于精确值数字,计算不会引入浮点错误。相反,使用精确精度。例如,MySQL 将
.0001这样的数字视为精确值,而不是近似值,将其累加 10,000 次的结果确切为1,而不是仅仅“接近”1 的值。 -
明确定义的四舍五入行为:对于精确值数字,
ROUND()的结果取决于其参数,而不取决于环境因素,如底层 C 库的工作方式。 -
平台独立性:对于精确数值的操作在不同平台(如 Windows 和 Unix)上是相同的。
-
对无效值处理的控制:溢出和除零可检测并可视为错误处理。例如,您可以将列值过大视为错误,而不是将值截断为列数据类型范围内。同样,您可以将除零视为错误,而不是产生
NULL结果的操作。采取哪种方法由服务器 SQL 模式设置决定。
以下讨论涵盖了精度数学的几个方面,包括与旧应用程序可能存在的不兼容性。最后,给出了一些示例,展示了 MySQL 如何精确处理数字操作。有关控制 SQL 模式的信息,请参见第 7.1.11 节,“服务器 SQL 模式”。
14.24.1 数值类型
原文:
dev.mysql.com/doc/refman/8.0/en/precision-math-numbers.html
精确值操作的精度数学范围包括精确值数据类型(整数和DECIMAL - DECIMAL, NUMERIC") 类型)和精确值数值文字。近似值数据类型和数值文字被处理为浮点数。
精确值数值文字具有整数部分、小数部分或两者兼有。它们可以带有符号。例如:1、.2、3.4、-5、-6.78、+9.10。
近似值数值文字用科学计数法表示,包括尾数和指数。尾数和/或指数部分可以带有符号。例如:1.2E3、1.2E-3、-1.2E3、-1.2E-3。
看似相似的两个数字可能会被处理得不同。例如,2.34是一个精确值(定点)数字,而2.34E0是一个近似值(浮点)数字。
DECIMAL - DECIMAL, NUMERIC") 数据类型是一个定点类型,计算是精确的。在 MySQL 中,DECIMAL - DECIMAL, NUMERIC") 类型有几个同义词:NUMERIC - DECIMAL, NUMERIC")、DEC - DECIMAL, NUMERIC")、FIXED - DECIMAL, NUMERIC")。整数类型也是精确值类型。
FLOAT - FLOAT, DOUBLE") 和 DOUBLE - FLOAT, DOUBLE") 数据类型是浮点类型,计算是近似的。在 MySQL 中,与 FLOAT - FLOAT, DOUBLE") 或 DOUBLE - FLOAT, DOUBLE") 同义的类型有 DOUBLE PRECISION - FLOAT, DOUBLE") 和 REAL - FLOAT, DOUBLE")。
14.24.2 DECIMAL 数据类型特性
译文:
dev.mysql.com/doc/refman/8.0/en/precision-math-decimal-characteristics.html
本节讨论 DECIMAL 数据类型(及其同义词)的特性,特别关注以下主题:
-
最大数字位数
-
存储格式
-
存储需求
-
DECIMAL列的非标准 MySQL 扩展的上限范围
DECIMAL 列的声明语法为 DECIMAL(*M*,*D*)。参数的值范围如下:
-
M是最大数字位数(精度)。它的范围是 1 到 65。 -
D是小数点右侧的数字位数(精度)。它的范围是 0 到 30,且不得大于*M*。
如果省略*D,默认值为 0。如果省略M*,默认值为 10。
M 的最大值为 65,意味着对 DECIMAL 值的计算精度可达 65 位。这个 65 位精度的限制也适用于精确值数值文字,因此这些文字的最大范围与以前不同。(DECIMAL 文字的文本长度也有限制;请参阅 第 14.24.3 节,“表达式处理”。)
DECIMAL 列的值以二进制格式存储,将九个十进制数字打包成 4 字节。每个值的整数部分和小数部分的存储需求分别确定。每个九位数字的倍数需要 4 字节,剩余的数字需要 4 字节的一部分。剩余数字所需的存储空间由以下表格给出。
| 剩余数字 | 字节数 |
|---|---|
| 0 | 0 |
| 1–2 | 1 |
| 3–4 | 2 |
| 5–6 | 3 |
| 7–9 | 4 |
例如,DECIMAL(18,9) 列有小数点两侧各有九位数字,因此整数部分和小数部分各需要 4 字节。DECIMAL(20,6) 列有十四位整数数字和六位小数数字。九位整数数字需要四字节,剩下的五位数字需要 3 字节。六位小数数字需要 3 字节。
DECIMAL 列不存储前导的 + 字符或 - 字符或前导的 0 数字。如果你将 +0003.1 插入到 DECIMAL(5,1) 列中,它将被存储为 3.1。对于负数,不会存储文字 - 字符。
DECIMAL 列不允许超出列定义所暗示的范围的值。例如,DECIMAL(3,0) 列支持 -999 到 999 的范围。DECIMAL(*M*,*D*) 列允许小数点左边最多 M - D 位数字。
SQL 标准要求 NUMERIC(*M*,*D*) 的精度 必须 恰好为 M 位数字。对于 DECIMAL(*M*,*D*),标准要求至少 M 位数字的精度,但允许更多。在 MySQL 中,DECIMAL(*M*,*D*) 和 NUMERIC(*M*,*D*) 是相同的,两者的精度都恰好为 M 位数字。
要了解 DECIMAL 值的内部格式的详细解释,请参阅 MySQL 源代码分发中的 strings/decimal.c 文件。该格式在 decimal2bin() 函数中进行了解释(附有示例)。
14.24.3 表达式处理
原文:
dev.mysql.com/doc/refman/8.0/en/precision-math-expressions.html
使用精确数学,尽可能使用给定的精确值作为数字。例如,在比较中使用的数字将完全按照给定值使用,而不会更改值。在严格 SQL 模式下,对于具有精确数据类型(DECIMAL或整数)的列进行INSERT时,如果数字在列范围内,则将插入其精确值。检索时,值应与插入的值相同。(如果未启用严格 SQL 模式,则允许对INSERT进行截断。)
数值表达式的处理取决于表达式包含的值的类型:
-
如果表达式中存在任何近似值,则表达式是近似的,并使用浮点运算进行评估。
-
如果表达式中没有近似值,则表达式只包含精确值。如果任何精确值包含小数部分(小数点后的值),则使用
DECIMAL精确算术进行评估,并具有 65 位数字的精度。术语“精确”受二进制表示的限制。例如,1.0/3.0可以在十进制表示中近似为.333...,但不能写成一个精确的数字,因此(1.0/3.0)*3.0不会精确评估为1.0。 -
否则,表达式只包含整数值。表达式是精确的,并使用整数运算进行评估,具有与
BIGINT(64 位)相同的精度。
如果数值表达式包含任何字符串,则它们将转换为双精度浮点值,并且表达式是近似的。
插入到数值列中受 SQL 模式的影响,该模式由sql_mode系统变量控制。(参见 Section 7.1.11, “Server SQL Modes”.)以下讨论提到了严格模式(由STRICT_ALL_TABLES或STRICT_TRANS_TABLES模式值选择)和ERROR_FOR_DIVISION_BY_ZERO。要打开所有限制,您可以简单地使用TRADITIONAL模式,其中包括严格模式值和ERROR_FOR_DIVISION_BY_ZERO:
SET sql_mode='TRADITIONAL';
如果将数字插入到精确类型列(DECIMAL - DECIMAL, NUMERIC") 或整数)中,如果在列范围和精度内,则插入其精确值。
如果小数部分的数字过多,将进行四舍五入并生成一个注释。四舍五入的处理如第 14.24.4 节,“四舍五入行为”中所述。由于小数部分的四舍五入而导致截断不是错误,即使在严格模式下也是如此。
如果整数部分的数字过多,则它太大(超出范围)并按以下方式处理:
-
如果未启用严格模式,则该值将被截断为最接近的合法值,并生成警告。
-
如果启用严格模式,则会发生溢出错误。
在 MySQL 8.0.31 之前,对于DECIMAL - DECIMAL, NUMERIC")文字,除了 65 位数字的精度限制外,还有一个限制文字长度的限制。如果值超过大约 80 个字符,可能会导致意外结果。例如:
mysql> SELECT
CAST(0000000000000000000000000000000000000000000000000000000000000000000000000000000020.01 AS DECIMAL(15,2)) as val;
+------------------+
| val |
+------------------+
| 9999999999999.99 |
+------------------+
1 row in set, 2 warnings (0.00 sec)
mysql> SHOW WARNINGS;
+---------+------+----------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------+
| Warning | 1292 | Truncated incorrect DECIMAL value: '20' |
| Warning | 1264 | Out of range value for column 'val' at row 1 |
+---------+------+----------------------------------------------+
2 rows in set (0.00 sec)
截至 MySQL 8.0.31,这不再是一个问题,如下所示:
mysql> SELECT
CAST(0000000000000000000000000000000000000000000000000000000000000000000000000000000020.01 AS DECIMAL(15,2)) as val;
+-------+
| val |
+-------+
| 20.01 |
+-------+
1 row in set (0.00 sec)
下溢不会被检测到,因此下溢处理是未定义的。
对于将字符串插入到数字列中,如果字符串具有非数字内容,则将字符串转换为数字的处理如下:
-
以非数字开头的字符串不能用作数字,在严格模式下会产生错误,否则会产生警告。这包括空字符串。
-
以数字开头的字符串可以转换,但尾随的非数字部分将被截断。如果被截断的部分包含除空格以外的任何内容,在严格模式下会产生错误,否则会产生警告。
默认情况下,除零操作会产生NULL结果且不会有警告。通过适当设置 SQL 模式,可以限制除零操作。
启用ERROR_FOR_DIVISION_BY_ZERO SQL 模式后,MySQL 会以不同方式处理除零操作:
-
如果未启用严格模式,将产生警告。
-
如果启用严格模式,则禁止涉及除零操作的插入和更新,并出现错误。
换句话说,涉及除零操作的插入和更新可以被视为错误,但这需要ERROR_FOR_DIVISION_BY_ZERO以及严格模式。
假设我们有以下语句:
INSERT INTO t SET i = 1/0;
这是严格模式和ERROR_FOR_DIVISION_BY_ZERO模式的组合情况。
sql_mode 值 | 结果 |
|---|---|
''(默认) | 无警告,无错误;i 被设置为 NULL。 |
| 严格 | 无警告,无错误;i 被设置为 NULL。 |
ERROR_FOR_DIVISION_BY_ZERO | 警告,没有错误;i 被设置为 NULL。 |
严格模式,ERROR_FOR_DIVISION_BY_ZERO | 错误条件;不插入任何行。 |
14.24.4 舍入行为
原文:
dev.mysql.com/doc/refman/8.0/en/precision-math-rounding.html
本节讨论了ROUND()函数的精确数学舍入以及对具有精确值类型(DECIMAL和整数)的列的插入。
ROUND()函数的舍入方式取决于其参数是精确还是近似值:
-
对于精确值数字,
ROUND()使用“四舍五入到最接近的一半”规则:具有大于或等于.5 的分数部分的值将向上舍入到下一个整数(如果为正数)或向下舍入到下一个整数(如果为负数)。 (换句话说,它远离零四舍五入。)具有小于.5 的分数部分的值将向下舍入到下一个整数(如果为正数)或向上舍入到下一个整数(如果为负数)。 (换句话说,它朝向零四舍五入。) -
对于近似值数字,结果取决于 C 库。在许多系统上,这意味着
ROUND()使用“四舍五入到最近的偶数”规则:一个具有恰好处于两个整数之间的分数部分的值将四舍五入为最接近的偶数整数。
以下示例显示了精确值和近似值之间舍入方式的不同:
mysql> SELECT ROUND(2.5), ROUND(25E-1);
+------------+--------------+
| ROUND(2.5) | ROUND(25E-1) |
+------------+--------------+
| 3 | 2 |
+------------+--------------+
对于插入到DECIMAL或整数列中,目标是精确数据类型,因此无论要插入的值是精确还是近似值,舍入都使用“远离零的一半”:
mysql> CREATE TABLE t (d DECIMAL(10,0));
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t VALUES(2.5),(2.5E0);
Query OK, 2 rows affected, 2 warnings (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 2
mysql> SHOW WARNINGS;
+-------+------+----------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------+
| Note | 1265 | Data truncated for column 'd' at row 1 |
| Note | 1265 | Data truncated for column 'd' at row 2 |
+-------+------+----------------------------------------+
2 rows in set (0.00 sec)
mysql> SELECT d FROM t;
+------+
| d |
+------+
| 3 |
| 3 |
+------+
2 rows in set (0.00 sec)
SHOW WARNINGS语句显示由于小数部分的舍入而生成的注释。这种截断不是错误,即使在严格的 SQL 模式下也是如此(请参阅第 14.24.3 节,“表达式处理”)。
14.24.5 精度数学示例
原文:
dev.mysql.com/doc/refman/8.0/en/precision-math-examples.html
本节提供了一些示例,展示了 MySQL 中精度数学查询结果的示例。这些示例演示了第 14.24.3 节,“表达式处理”和第 14.24.4 节,“四舍五入行为”中描述的原则。
示例 1。在可能的情况下,数字使用其给定的精确值:
mysql> SELECT (.1 + .2) = .3;
+----------------+
| (.1 + .2) = .3 |
+----------------+
| 1 |
+----------------+
对于浮点值,结果是不精确的:
mysql> SELECT (.1E0 + .2E0) = .3E0;
+----------------------+
| (.1E0 + .2E0) = .3E0 |
+----------------------+
| 0 |
+----------------------+
另一种看到精确值和近似值处理差异的方法是多次向总和中添加一个小数。考虑以下存储过程,它将.0001添加到一个变量 1,000 次。
CREATE PROCEDURE p ()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE d DECIMAL(10,4) DEFAULT 0;
DECLARE f FLOAT DEFAULT 0;
WHILE i < 10000 DO
SET d = d + .0001;
SET f = f + .0001E0;
SET i = i + 1;
END WHILE;
SELECT d, f;
END;
对于d和f的总和在逻辑上应该为 1,但这仅适用于十进制计算。浮点计算引入了小误差:
+--------+------------------+
| d | f |
+--------+------------------+
| 1.0000 | 0.99999999999991 |
+--------+------------------+
示例 2。乘法使用标准 SQL 所需的比例执行。也就是说,对于具有比例S1和S2的两个数字X1和X2,结果的比例为S1 + S2:
mysql> SELECT .01 * .01;
+-----------+
| .01 * .01 |
+-----------+
| 0.0001 |
+-----------+
示例 3。精确值数字的四舍五入行为是明确定义的:
四舍五入行为(例如,使用ROUND()函数)独立于底层 C 库的实现,这意味着结果在不同平台上是一致的。
-
精确值列(
DECIMAL- DECIMAL, NUMERIC") 和整数)以及精确值数字的四舍五入使用“远离零的方向”规则。具有小数部分为.5 或更大的值将远离零四舍五入到最近的整数,如下所示:mysql> SELECT ROUND(2.5), ROUND(-2.5); +------------+-------------+ | ROUND(2.5) | ROUND(-2.5) | +------------+-------------+ | 3 | -3 | +------------+-------------+ -
浮点值的四舍五入使用 C 库,许多系统上使用“四舍五入到最近偶数”的规则。具有恰好处于两个整数之间的小数部分的值将四舍五入为最近的偶数:
mysql> SELECT ROUND(2.5E0), ROUND(-2.5E0); +--------------+---------------+ | ROUND(2.5E0) | ROUND(-2.5E0) | +--------------+---------------+ | 2 | -2 | +--------------+---------------+
示例 4。在严格模式下,插入超出列范围的值会导致错误,而不是截断为合法值。
当 MySQL 未在严格模式下运行时,会发生截断为合法值的情况:
mysql> SET sql_mode='';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE t (i TINYINT);
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO t SET i = 128;
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> SELECT i FROM t;
+------+
| i |
+------+
| 127 |
+------+
1 row in set (0.00 sec)
但是,如果启用了严格模式,则会发生错误:
mysql> SET sql_mode='STRICT_ALL_TABLES';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE t (i TINYINT);
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t SET i = 128;
ERROR 1264 (22003): Out of range value adjusted for column 'i' at row 1
mysql> SELECT i FROM t;
Empty set (0.00 sec)
示例 5:在严格模式下,并且设置了ERROR_FOR_DIVISION_BY_ZERO,除以零会导致错误,而不是结果为NULL。
在非严格模式下,除以零的结果为NULL:
mysql> SET sql_mode='';
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE t (i TINYINT);
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t SET i = 1 / 0;
Query OK, 1 row affected (0.00 sec)
mysql> SELECT i FROM t;
+------+
| i |
+------+
| NULL |
+------+
1 row in set (0.03 sec)
但是,如果启用了正确的 SQL 模式,则除以零会导致错误:
mysql> SET sql_mode='STRICT_ALL_TABLES,ERROR_FOR_DIVISION_BY_ZERO';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE t (i TINYINT);
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t SET i = 1 / 0;
ERROR 1365 (22012): Division by 0
mysql> SELECT i FROM t;
Empty set (0.01 sec)
示例 6。精确值文字被评估为精确值。
近似值文字使用浮点数进行评估,但精确值文字被处理为DECIMAL - DECIMAL, NUMERIC"):
mysql> CREATE TABLE t SELECT 2.5 AS a, 25E-1 AS b;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> DESCRIBE t;
+-------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+---------+-------+
| a | decimal(2,1) unsigned | NO | | 0.0 | |
| b | double | NO | | 0 | |
+-------+-----------------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
示例 7。如果聚合函数的参数是精确数值类型,则结果也是精确数值类型,其精度至少与参数相同。
考虑以下语句:
mysql> CREATE TABLE t (i INT, d DECIMAL, f FLOAT);
mysql> INSERT INTO t VALUES(1,1,1);
mysql> CREATE TABLE y SELECT AVG(i), AVG(d), AVG(f) FROM t;
结果仅对浮点参数为双精度。对于精确类型参数,结果也是精确类型:
mysql> DESCRIBE y;
+--------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------------+------+-----+---------+-------+
| AVG(i) | decimal(14,4) | YES | | NULL | |
| AVG(d) | decimal(14,4) | YES | | NULL | |
| AVG(f) | double | YES | | NULL | |
+--------+---------------+------+-----+---------+-------+
结果仅对浮点参数为双精度。对于精确类型参数,结果也是精确类型。
第十五章 SQL 语句
目录
15.1 数据定义语句
15.1.1 原子数据定义语句支持
15.1.2 ALTER DATABASE 语句
15.1.3 ALTER EVENT 语句
15.1.4 ALTER FUNCTION 语句
15.1.5 ALTER INSTANCE 语句
15.1.6 ALTER LOGFILE GROUP 语句
15.1.7 ALTER PROCEDURE 语句
15.1.8 ALTER SERVER 语句
15.1.9 ALTER TABLE 语句
15.1.10 ALTER TABLESPACE 语句
15.1.11 ALTER VIEW 语句
15.1.12 CREATE DATABASE 语句
15.1.13 CREATE EVENT 语句
15.1.14 CREATE FUNCTION 语句
15.1.15 CREATE INDEX 语句
15.1.16 CREATE LOGFILE GROUP 语句
15.1.17 CREATE PROCEDURE 和 CREATE FUNCTION 语句
15.1.18 CREATE SERVER 语句
15.1.19 CREATE SPATIAL REFERENCE SYSTEM 语句
15.1.20 CREATE TABLE 语句
15.1.21 CREATE TABLESPACE 语句
15.1.22 CREATE TRIGGER 语句
15.1.23 CREATE VIEW 语句
15.1.24 DROP DATABASE 语句
15.1.25 DROP EVENT 语句
15.1.26 DROP FUNCTION 语句
15.1.27 DROP INDEX 语句
15.1.28 DROP LOGFILE GROUP 语句
15.1.29 DROP PROCEDURE 和 DROP FUNCTION 语句
15.1.30 DROP SERVER 语句
15.1.31 DROP SPATIAL REFERENCE SYSTEM 语句
15.1.32 DROP TABLE 语句
15.1.33 DROP TABLESPACE 语句
15.1.34 DROP TRIGGER 语句
15.1.35 DROP VIEW 语句
15.1.36 重命名表 语句
15.1.37 TRUNCATE TABLE 语句
15.2 数据操作语句
15.2.1 CALL 语句
15.2.2 DELETE 语句
15.2.3 DO 语句
15.2.4 EXCEPT 子句
15.2.5 HANDLER 语句
15.2.6 IMPORT TABLE 语句
15.2.7 INSERT 语句
15.2.8 INTERSECT 子句
15.2.9 LOAD DATA 语句
15.2.10 LOAD XML 语句
15.2.11 带括号的查询表达式
15.2.12 REPLACE 语句
15.2.13 SELECT 语句
15.2.14 使用 UNION、INTERSECT 和 EXCEPT 的集合操作
15.2.15 子查询
15.2.16 TABLE 语句
15.2.17 UPDATE 语句
15.2.18 UNION 子句
15.2.19 VALUES 语句
15.2.20 WITH(公共表达式)
15.3 事务和锁定语句
15.3.1 START TRANSACTION、COMMIT 和 ROLLBACK 语句
15.3.2 无法回滚的语句
15.3.3 导致隐式提交的语句
15.3.4 SAVEPOINT、ROLLBACK TO SAVEPOINT 和 RELEASE SAVEPOINT 语句
15.3.5 LOCK INSTANCE FOR BACKUP 和 UNLOCK INSTANCE 语句
15.3.6 LOCK TABLES 和 UNLOCK TABLES 语句
15.3.7 SET TRANSACTION 语句
15.3.8 XA 事务
15.4 复制语句
15.4.1 控制源服务器的 SQL 语句
15.4.2 控制副本服务器的 SQL 语句
15.4.3 控制组复制的 SQL 语句
15.5 预处理语句
15.5.1 PREPARE 语句
15.5.2 EXECUTE 语句
15.5.3 DEALLOCATE PREPARE 语句
15.6 复合语句语法
15.6.1 BEGIN ... END 复合语句
15.6.2 语句标签
15.6.3 DECLARE 语句
15.6.4 存储程序中的变量
15.6.5 流程控制语句
15.6.6 游标
15.6.7 条件处理
15.6.8 条件处理的限制
15.7 数据库管理语句
15.7.1 账户管理语句
15.7.2 资源组管理语句
15.7.3 表维护语句
15.7.4 组件、插件和可加载函数语句
15.7.5 CLONE 语句
15.7.6 SET 语句
15.7.7 SHOW 语句
15.7.8 其他管理语句
15.8 实用程序语句
15.8.1 DESCRIBE 语句
15.8.2 EXPLAIN 语句
15.8.3 HELP 语句
15.8.4 USE 语句
本章描述了 MySQL 支持的 SQL 语句的语法。
15.1 数据定义语句
原文:
dev.mysql.com/doc/refman/8.0/en/sql-data-definition-statements.html
15.1.1 原子数据定义语句支持
15.1.2 修改数据库语句
15.1.3 修改事件语句
15.1.4 修改函数语句
15.1.5 修改实例语句
15.1.6 修改日志文件组语句
15.1.7 修改存储过程语句
15.1.8 修改服务器语句
15.1.9 修改表语句
15.1.10 修改表空间语句
15.1.11 修改视图语句
15.1.12 创建数据库语句
15.1.13 创建事件语句
15.1.14 创建函数语句
15.1.15 创建索引语句
15.1.16 创建日志文件组语句
15.1.17 创建存储过程和创建函数语句
15.1.18 创建服务器语句
15.1.19 创建空间参考系统语句
15.1.20 创建表语句
15.1.21 创建表空间语句
15.1.22 创建触发器语句
15.1.23 创建视图语句
15.1.24 删除数据库语句
15.1.25 删除事件语句
15.1.26 删除函数语句
15.1.27 删除索引语句
15.1.28 删除日志文件组语句
15.1.29 删除存储过程和删除函数语句
15.1.30 删除服务器语句
15.1.31 删除空间参考系统语句
15.1.32 删除表语句
15.1.33 删除表空间语句
15.1.34 删除触发器语句
15.1.35 删除视图语句
15.1.36 重命名表语句
15.1.37 截断表语句
15.1.1 原子数据定义语句支持
MySQL 8.0 支持原子数据定义语言(DDL)语句。这一特性被称为原子 DDL。原子 DDL 语句将与 DDL 操作相关的数据字典更新、存储引擎操作和二进制日志写入合并为单个原子操作。该操作要么被提交,适用更改被持久化到数据字典、存储引擎和二进制日志中,要么被回滚,即使服务器在操作过程中停止。
注意
原子 DDL不是事务性 DDL。DDL 语句,无论是原子的还是其他的,都会隐式结束当前会话中活动的任何事务,就好像在执行该语句之前执行了COMMIT一样。这意味着 DDL 语句不能在另一个事务中执行,在事务控制语句中(如START TRANSACTION ... COMMIT)中执行,或者与同一事务中的其他语句组合。
MySQL 8.0 中引入的 MySQL 数据字典使原子 DDL 成为可能。在早期的 MySQL 版本中,元数据存储在元数据文件、非事务表和存储引擎特定的字典中,这需要中间提交。MySQL 数据字典提供的集中式、事务性元数据存储消除了这一障碍,使得重构 DDL 语句操作成为原子操作成为可能。
本节中以下主题描述了原子 DDL 特性:
-
支持的 DDL 语句
-
原子 DDL 特性
-
DDL 语句行为变化
-
存储引擎支持
-
查看 DDL 日志
支持的 DDL 语句
原子 DDL 特性支持表和非表 DDL 语句。与表相关的 DDL 操作需要存储引擎支持,而非表 DDL 操作则不需要。目前,只有InnoDB存储引擎支持原子 DDL。
-
支持的表 DDL 语句包括对数据库、表空间、表和索引的
CREATE、ALTER和DROP语句,以及TRUNCATE TABLE语句。 -
支持的非表 DDL 语句包括:
-
CREATE和DROP语句,以及(如果适用)用于存储过程、触发器、视图和可加载函数的ALTER语句。 -
帐户管理语句:用户和角色的
CREATE,ALTER,DROP,以及如果适用的RENAME语句,以及GRANT和REVOKE语句。
-
原子 DDL 功能不支持以下语句:
-
与
InnoDB不同的存储引擎涉及的与表相关的 DDL 语句。 -
INSTALL PLUGIN和UNINSTALL PLUGIN语句。 -
INSTALL COMPONENT和UNINSTALL COMPONENT语句。 -
CREATE SERVER,ALTER SERVER和DROP SERVER语句。
原子 DDL 特性
原子 DDL 语句的特性包括以下内容:
-
元数据更新,二进制日志写入和存储引擎操作(如果适用)被合并为单个原子操作。
-
在 DDL 操作期间,SQL 层没有中间提交。
-
如果适用:
-
数据字典,例程,事件和可加载函数缓存的状态与 DDL 操作的状态一致,这意味着缓存将根据 DDL 操作是否成功完成或回滚而更新。
-
DDL 操作涉及的存储引擎方法不执行中间提交,并且存储引擎将自身注册为 DDL 操作的一部分。
-
存储引擎支持 DDL 操作的重做和回滚,这是在 DDL 操作的后 DDL阶段执行的。
-
-
DDL 操作的可见行为是原子的,这改变了一些 DDL 语句的行为。请参阅 DDL 语句行为的变化。
DDL 语句行为的变化
本节描述了由于引入原子 DDL 支持而导致的 DDL 语句行为的变化。
-
如果所有命名表使用支持原子 DDL 的存储引擎,则
DROP TABLE操作是完全原子的。该语句要么成功删除所有表,要么回滚。如果命名表不存在,则
DROP TABLE将失败并显示错误,无论存储引擎如何,都不会进行任何更改。下面的示例演示了行为的变化,DROP TABLE语句因为命名表不存在而失败:mysql> CREATE TABLE t1 (c1 INT); mysql> DROP TABLE t1, t2; ERROR 1051 (42S02): Unknown table 'test.t2' mysql> SHOW TABLES; +----------------+ | Tables_in_test | +----------------+ | t1 | +----------------+在引入原子 DDL 之前,
DROP TABLE对于不存在的命名表报告错误,但对于存在的命名表成功:mysql> CREATE TABLE t1 (c1 INT); mysql> DROP TABLE t1, t2; ERROR 1051 (42S02): Unknown table 'test.t2' mysql> SHOW TABLES; Empty set (0.00 sec)注意
由于这种行为变化,当在 MySQL 5.7 复制源服务器上复制到 MySQL 8.0 副本时,部分完成的
DROP TABLE语句会失败。为避免此失败场景,在DROP TABLE语句中使用IF EXISTS语法,以防止对不存在的表发生错误。 -
如果所有表使用支持原子 DDL 的存储引擎,则
DROP DATABASE是原子的。该语句要么成功删除所有对象,要么回滚。然而,从文件系统中删除数据库目录是最后发生的,不是原子操作的一部分。如果由于文件系统错误或服务器停止而导致无法删除数据库目录,则不会回滚DROP DATABASE事务。 -
对于不使用支持原子 DDL 的存储引擎的表,表删除发生在原子
DROP TABLE或DROP DATABASE事务之外。这种表删除会单独写入二进制日志,这限制了在中断的DROP TABLE或DROP DATABASE操作中存储引擎、数据字典和二进制日志之间的差异至多为一个表。对于删除多个表的操作,不使用支持原子 DDL 的存储引擎的表会在支持原子 DDL 的表之前被删除。 -
对于使用支持原子 DDL 的存储引擎的表,
CREATE TABLE、ALTER TABLE、RENAME TABLE、TRUNCATE TABLE、CREATE TABLESPACE和DROP TABLESPACE操作在操作过程中如果服务器停止,要么完全提交,要么回滚。在早期的 MySQL 版本中,这些操作的中断可能导致存储引擎、数据字典和二进制日志之间的差异,或留下孤立文件。只有所有命名表使用支持原子 DDL 的存储引擎时,RENAME TABLE操作才是原子的。 -
从 MySQL 8.0.21 开始,在支持原子 DDL 的存储引擎上,当使用基于行的复制时,
CREATE TABLE ... SELECT语句被记录为二进制日志中的一个事务。以前,它被记录为两个事务,一个用于创建表,另一个用于插入数据。在两个事务之间或插入数据时发生服务器故障可能导致复制一个空表。随着原子 DDL 支持的引入,CREATE TABLE ... SELECT语句现在对基于行的复制是安全的,并且允许在基于 GTID 的复制中使用。在支持原子 DDL 和外键约束的存储引擎上,在使用基于行的复制时,不允许在
CREATE TABLE ... SELECT语句中创建外键约束。可以稍后使用ALTER TABLE添加外键约束。当
CREATE TABLE ... SELECT作为原子操作应用时,插入数据时会在表上持有元数据锁,这会阻止在操作期间对表的并发访问。 -
如果命名视图不存在,
DROP VIEW失败,并且不会进行任何更改。行为变更在这个例子中得到展示,DROP VIEW语句因为命名视图不存在而失败:mysql> CREATE VIEW test.viewA AS SELECT * FROM t; mysql> DROP VIEW test.viewA, test.viewB; ERROR 1051 (42S02): Unknown table 'test.viewB' mysql> SHOW FULL TABLES IN test WHERE TABLE_TYPE LIKE 'VIEW'; +----------------+------------+ | Tables_in_test | Table_type | +----------------+------------+ | viewA | VIEW | +----------------+------------+在引入原子 DDL 之前,
DROP VIEW对不存在的命名视图返回错误,但对存在的命名视图成功:mysql> CREATE VIEW test.viewA AS SELECT * FROM t; mysql> DROP VIEW test.viewA, test.viewB; ERROR 1051 (42S02): Unknown table 'test.viewB' mysql> SHOW FULL TABLES IN test WHERE TABLE_TYPE LIKE 'VIEW'; Empty set (0.00 sec)注意
由于这种行为变更,在 MySQL 5.7 复制源服务器上部分完成的
DROP VIEW操作在 MySQL 8.0 复制品上复制时会失败。为避免这种失败场景,在DROP VIEW语句中使用IF EXISTS语法,以防止对不存在的视图发生错误。 -
部分执行账户管理语句不再被允许。如果发生错误,账户管理语句要么对所有命名用户成功,要么回滚并且没有效果。在早期的 MySQL 版本中,命名多个用户的账户管理语句可能对一些用户成功,对另一些用户失败。
行为变更在这个例子中得到展示,第二个
CREATE USER语句返回错误但失败,因为它无法对所有命名用户成功。mysql> CREATE USER userA; mysql> CREATE USER userA, userB; ERROR 1396 (HY000): Operation CREATE USER failed for 'userA'@'%' mysql> SELECT User FROM mysql.user WHERE User LIKE 'user%'; +-------+ | User | +-------+ | userA | +-------+在引入原子 DDL 之前,第二个
CREATE USER语句对于不存在的命名用户返回错误,但对于已存在的命名用户成功:mysql> CREATE USER userA; mysql> CREATE USER userA, userB; ERROR 1396 (HY000): Operation CREATE USER failed for 'userA'@'%' mysql> SELECT User FROM mysql.user WHERE User LIKE 'user%'; +-------+ | User | +-------+ | userA | | userB | +-------+注意
由于这种行为变化,MySQL 5.7 复制源服务器上部分完成的帐户管理语句在 MySQL 8.0 副本上复制时会失败。为避免此失败场景,在帐户管理语句中使用
IF EXISTS或IF NOT EXISTS语法,适当地防止与命名用户相关的错误。
存储引擎支持
目前,只有InnoDB存储引擎支持原子 DDL。不支持原子 DDL 的存储引擎不受 DDL 原子性的约束。涉及免除的存储引擎的 DDL 操作仍然可能引入不一致性,当操作被中断或仅部分完成时可能发生。
为支持 DDL 操作的重做和回滚,InnoDB将 DDL 日志写入mysql.innodb_ddl_log表,这是一个隐藏的数据字典表,驻留在mysql.ibd数据字典表空间中。
要查看在 DDL 操作期间写入mysql.innodb_ddl_log表的 DDL 日志,请启用innodb_print_ddl_logs配置选项。有关更多信息,请参见查看 DDL 日志。
注意
对于对mysql.innodb_ddl_log表的更改的重做日志会立即刷新到磁盘,不受innodb_flush_log_at_trx_commit设置的影响。立即刷新重做日志可以避免数据文件被 DDL 操作修改,但由于这些操作导致的对mysql.innodb_ddl_log表的重做日志未持久化到磁盘。这种情况可能导致回滚或恢复时出现错误。
InnoDB存储引擎在阶段中执行 DDL 操作。DDL 操作,如ALTER TABLE可能在提交阶段之前多次执行准备和执行阶段。
-
准备: 创建所需对象并将 DDL 日志写入
mysql.innodb_ddl_log表。DDL 日志定义了如何前滚和回滚 DDL 操作。 -
执行: 执行 DDL 操作。例如,执行
CREATE TABLE操作的创建例程。 -
提交: 更新数据字典并提交数据字典事务。
-
后 DDL:从
mysql.innodb_ddl_log表中重放和删除 DDL 日志。为了确保可以安全地执行回滚而不引入不一致性,文件操作(如重命名或删除数据文件)在这个最终阶段执行。此阶段还会从mysql.innodb_dynamic_metadata数据字典表中删除DROP TABLE、TRUNCATE TABLE和其他重建表的 DDL 操作的动态元数据。
无论 DDL 操作是提交还是回滚,在后 DDL阶段都会重放和删除mysql.innodb_ddl_log表中的 DDL 日志。只有在服务器在 DDL 操作期间停止时,DDL 日志才应该保留在mysql.innodb_ddl_log表中。在这种情况下,恢复后会重放和删除 DDL 日志。
在恢复情况下,当服务器重新启动时,DDL 操作可能会提交或回滚。如果在 DDL 操作的提交阶段执行的数据字典事务存在于重做日志和二进制日志中,则认为操作成功,并向前滚动。否则,当InnoDB重放数据字典重做日志时,不完整的数据字典事务将被回滚,DDL 操作将被回滚。
查看 DDL 日志
要查看写入mysql.innodb_ddl_log数据字典表的与涉及InnoDB存储引擎的原子 DDL 操作相关的 DDL 日志,请启用innodb_print_ddl_logs以使 MySQL 将 DDL 日志写入stderr。根据主机操作系统和 MySQL 配置,stderr可能是错误日志、终端或控制台窗口。请参见第 7.4.2.2 节,“默认错误日志目标配置”。
InnoDB将 DDL 日志写入mysql.innodb_ddl_log表,以支持 DDL 操作的重做和回滚。mysql.innodb_ddl_log表是一个隐藏的数据字典表,驻留在mysql.ibd数据字典表空间中。与其他隐藏的数据字典表一样,在非调试版本的 MySQL 中无法直接访问mysql.innodb_ddl_log表。(参见第 16.1 节,“数据字典模式”。)mysql.innodb_ddl_log表的结构对应于以下定义:
CREATE TABLE mysql.innodb_ddl_log (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
thread_id BIGINT UNSIGNED NOT NULL,
type INT UNSIGNED NOT NULL,
space_id INT UNSIGNED,
page_no INT UNSIGNED,
index_id BIGINT UNSIGNED,
table_id BIGINT UNSIGNED,
old_file_path VARCHAR(512) COLLATE utf8mb4_bin,
new_file_path VARCHAR(512) COLLATE utf8mb4_bin,
KEY(thread_id)
);
-
id: 用于标识 DDL 日志记录的唯一标识符。 -
thread_id:每个 DDL 日志记录都被分配一个thread_id,用于重放和删除属于特定 DDL 操作的 DDL 日志。涉及多个数据文件操作的 DDL 操作会生成多个 DDL 日志记录。 -
type:DDL 操作类型。类型包括FREE(删除索引树)、DELETE(删除文件)、RENAME(重命名文件)或DROP(从mysql.innodb_dynamic_metadata数据字典表中删除元数据)。 -
space_id:表空间 ID。 -
page_no:包含分配信息的页面;例如,索引树根页面。 -
index_id:索引 ID。 -
table_id:表 ID。 -
old_file_path:旧表空间文件路径。用于创建或删除表空间文件的 DDL 操作;也用于重命名表空间的 DDL 操作。 -
new_file_path:新表空间文件路径。用于重命名表空间文件的 DDL 操作。
这个示例演示了启用innodb_print_ddl_logs以查看写入strderr的 DDL 日志,用于CREATE TABLE操作。
mysql> SET GLOBAL innodb_print_ddl_logs=1;
mysql> CREATE TABLE t1 (c1 INT) ENGINE = InnoDB;
[Note] [000000] InnoDB: DDL log insert : [DDL record: DELETE SPACE, id=18, thread_id=7,
space_id=5, old_file_path=./test/t1.ibd]
[Note] [000000] InnoDB: DDL log delete : by id 18
[Note] [000000] InnoDB: DDL log insert : [DDL record: REMOVE CACHE, id=19, thread_id=7,
table_id=1058, new_file_path=test/t1]
[Note] [000000] InnoDB: DDL log delete : by id 19
[Note] [000000] InnoDB: DDL log insert : [DDL record: FREE, id=20, thread_id=7,
space_id=5, index_id=132, page_no=4]
[Note] [000000] InnoDB: DDL log delete : by id 20
[Note] [000000] InnoDB: DDL log post ddl : begin for thread id : 7
[Note] [000000] InnoDB: DDL log post ddl : end for thread id : 7
15.1.2 ALTER DATABASE 语句
ALTER {DATABASE | SCHEMA} [*db_name*]
*alter_option* ...
*alter_option*: {
[DEFAULT] CHARACTER SET [=] *charset_name*
| [DEFAULT] COLLATE [=] *collation_name*
| [DEFAULT] ENCRYPTION [=] {'Y' | 'N'}
| READ ONLY [=] {DEFAULT | 0 | 1}
}
ALTER DATABASE允许您更改数据库的整体特性。这些特性存储在数据字典中。此语句需要对数据库具有ALTER权限。ALTER SCHEMA是ALTER DATABASE的同义词。
如果省略了数据库名称,则该语句适用于默认数据库。在这种情况下,如果没有默认数据库,则会发生错误。
对于语句中省略的任何*alter_option*,数据库将保留其当前选项值,但更改字符集可能会更改校对规则,反之亦然。
-
字符集和校对选项
-
加密选项
-
只读选项
字符集和校对选项
CHARACTER SET选项更改默认数据库字符集。COLLATE选项更改默认数据库校对规则。有关字符集和校对规则名称的信息,请参见第十二章,字符集,校对规则,Unicode。
要查看可用的字符集和校对规则,请分别使用SHOW CHARACTER SET和SHOW COLLATION语句。请参见第 15.7.7.3 节,“SHOW CHARACTER SET 语句”,以及第 15.7.7.4 节,“SHOW COLLATION 语句”。
在创建存储过程时使用数据库默认值的存储过程将这些默认值作为其定义的一部分。 (在存储过程中,如果未明确指定字符集或校对规则,则具有字符数据类型的变量将使用数据库默认值。请参见第 15.1.17 节,“CREATE PROCEDURE 和 CREATE FUNCTION 语句”。)如果更改数据库的默认字符集或校对规则,则必须删除并重新创建任何要使用新默认值的存储过程。
加密选项
ENCRYPTION选项是在 MySQL 8.0.16 中引入的,定义了默认数据库加密,该加密会被创建在数据库中的表继承。允许的值为'Y'(启用加密)和'N'(禁用加密)。
mysql系统模式无法设置为默认加密。其中现有的表属于通用mysql表空间,可能已加密。information_schema仅包含视图。不可能在其中创建任何表。磁盘上没有任何内容可供加密。performance_schema中的所有表都使用PERFORMANCE_SCHEMA引擎,纯粹是内存中的。不可能在其中创建任何其他表。磁盘上没有任何内容可供加密。
仅新创建的表继承默认数据库加密。对于与数据库关联的现有表,它们的加密保持不变。如果启用了table_encryption_privilege_check系统变量,则需要TABLE_ENCRYPTION_ADMIN权限来指定与default_table_encryption系统变量的值不同的默认加密设置。有关更多信息,请参阅为模式和通用表空间定义加密默认值。
只读选项
READ ONLY选项在 MySQL 8.0.22 中引入,控制是否允许修改数据库及其中的对象。允许的值为DEFAULT或0(非只读)和1(只读)。此选项对数据库迁移很有用,因为启用READ ONLY的数据库可以在迁移至另一个 MySQL 实例时,无需担心数据库在操作期间会被更改。
对于 NDB Cluster,在一个mysqld服务器上将数据库设置为只读会同步到同一集群中的其他mysqld服务器,使得数据库在所有mysqld服务器上都变为只读。
如果启用了READ ONLY选项,则会在INFORMATION_SCHEMA的SCHEMATA_EXTENSIONS表中显示。请参阅第 28.3.32 节,“INFORMATION_SCHEMA SCHEMATA_EXTENSIONS 表”。
不能为这些系统模式启用READ ONLY选项:mysql、information_schema、performance_schema。
在ALTER DATABASE语句中,READ ONLY选项与其他实例及其他选项的交互如下:
-
如果多个
READ ONLY实例发生冲突(例如,READ ONLY = 1 READ ONLY = 0),则会发生错误。 -
即使是只读数据库,也允许包含(不冲突的)
READ ONLY选项的ALTER DATABASE语句。 -
如果数据库在语句执行前或执行后的只读状态允许修改,则允许将(不冲突的)
READ ONLY选项与其他选项混合使用。如果数据库在执行前后的只读状态都禁止更改,则会发生错误。无论数据库是否只读,此语句都会成功:
ALTER DATABASE mydb READ ONLY = 0 DEFAULT COLLATE utf8mb4_bin;如果数据库不是只读,则此语句成功,但如果数据库已经是只读,则失败:
ALTER DATABASE mydb READ ONLY = 1 DEFAULT COLLATE utf8mb4_bin;
启用READ ONLY会影响数据库的所有用户,以下情况不受只读检查限制:
-
作为服务器初始化、重启、升级或复制的一部分由服务器执行的语句。
-
由
init_file系统变量在服务器启动时命名的文件中的语句。 -
TEMPORARY表;在只读数据库中可以创建、修改、删除和写入TEMPORARY表。 -
NDB Cluster 非 SQL 插入和更新。
除了刚刚列出的例外操作外,启用READ ONLY会禁止对数据库及其对象(包括定义、数据和元数据)进行写操作。以下列表详细说明受影响的 SQL 语句和操作:
-
数据库本身:
-
CREATE DATABASE -
ALTER DATABASE(除了更改READ ONLY选项) -
DROP DATABASE
-
-
视图:
-
CREATE VIEW -
ALTER VIEW -
DROP VIEW -
从调用具有副作用的函数的视图中进行选择。
-
更新可更新的视图。
-
如果影响只读数据库中视图的元数据(例如,使视图有效或无效)的对象在可写数据库中创建或删除,则会拒绝这些语句。
-
-
存储过程:
-
CREATE PROCEDURE -
DROP PROCEDURE -
CALL(具有副作用的过程调用) -
CREATE FUNCTION -
DROP FUNCTION -
SELECT(具有副作用的函数选择) -
对于存储过程和函数,只读检查遵循预锁定行为。对于
CALL语句,只读检查是基于每个语句进行的,因此如果某个有条件执行的写入只读数据库的语句实际上没有执行,调用仍然成功。另一方面,在SELECT中调用的函数,函数体的执行是在预锁定模式下进行的。只要函数中的某个语句写入只读数据库,无论该语句是否实际执行,函数的执行都会因错误而失败。
-
-
触发器:
-
CREATE TRIGGER -
DROP TRIGGER -
触发器调用。
-
-
事件:
-
CREATE EVENT -
ALTER EVENT -
DROP EVENT -
事件执行:
-
在数据库中执行事件会失败,因为这会更改最后执行时间戳,这是存储在数据字典中的事件元数据。事件执行失败还会导致事件调度程序停止。
-
如果事件写入只读数据库中的对象,则事件执行将因错误而失败,但事件调度程序不会停止。
-
-
-
表:
-
CREATE TABLE -
ALTER TABLE -
CREATE INDEX -
DROP INDEX -
RENAME TABLE -
TRUNCATE TABLE -
DROP TABLE -
DELETE -
INSERT -
IMPORT TABLE -
LOAD DATA -
LOAD XML -
REPLACE -
UPDATE -
对于级联外键,如果子表位于只读数据库中,则父表的更新和删除将被拒绝,即使子表并未直接受到影响。
-
对于
MERGE表,例如CREATE TABLE s1.t(i int) ENGINE MERGE UNION (s2.t, s3.t), INSERT_METHOD=...,以下行为适用:-
如果插入
MERGE表(INSERT into s1.t)时,s1、s2、s3中至少有一个是只读的,则无论插入方法如何,插入都会失败。即使实际上插入的数据最终会进入可写表中,也会被拒绝。 -
删除
MERGE表(DROP TABLE s1.t)成功,只要s1不是只读的。允许删除引用只读数据库的MERGE表。
-
-
一个ALTER DATABASE语句会阻塞,直到所有已经访问正在被更改的数据库中的对象的并发事务都已提交。相反,访问正在被并发ALTER DATABASE更改的数据库中的对象的写事务会阻塞,直到ALTER DATABASE已提交。
如果使用克隆插件克隆本地或远程数据目录,则克隆中的数据库保留其在源数据目录中的只读状态。只读状态不会影响克隆过程本身。如果不希望在克隆中具有相同的数据库只读状态,则必须在克隆过程完成后显式更改选项,使用克隆上的ALTER DATABASE操作。
当从捐赠者克隆到接收者时,如果接收者有一个用户数据库是只读的,克隆将因错误消息而失败。在使数据库可写后,可以重试克隆。
READ ONLY允许用于ALTER DATABASE,但不允许用于CREATE DATABASE。然而,对于只读数据库,由SHOW CREATE DATABASE生成的语句在注释中包含READ ONLY=1,以指示其只读状态:
mysql> ALTER DATABASE mydb READ ONLY = 1;
mysql> SHOW CREATE DATABASE mydb\G
*************************** 1\. row ***************************
Database: mydb
Create Database: CREATE DATABASE `mydb`
/*!40100 DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci */
/*!80016 DEFAULT ENCRYPTION='N' */
/* READ ONLY = 1 */
如果服务器执行包含这样一个注释的CREATE DATABASE语句,服务器会忽略该注释,READ ONLY选项不会被处理。这对于mysqldump和mysqlpump有影响,它们使用SHOW CREATE DATABASE生成转储输出中的CREATE DATABASE语句:
-
在转储文件中,只读数据库的
CREATE DATABASE语句包含了注释的READ ONLY选项。 -
转储文件可以像往常一样还原,但由于服务器忽略了注释的
READ ONLY选项,还原的数据库不是只读的。如果在还原后要使数据库变为只读,必须手动执行ALTER DATABASE来实现。
假设mydb是只读的,并且您将其转储如下:
$> mysqldump --databases mydb > mydb.sql
之后的还原操作必须在mydb仍然是只读时跟随ALTER DATABASE:
$> mysql
mysql> SOURCE mydb.sql;
mysql> ALTER DATABASE mydb READ ONLY = 1;
MySQL 企业备份不受此问题影响。它备份和恢复只读数据库就像其他数据库一样,但如果备份时启用了READ ONLY选项,则在恢复时会启用该选项。
ALTER DATABASE会被写入二进制日志,因此在复制源服务器上对READ ONLY选项进行更改也会影响副本。为了防止这种情况发生,在执行ALTER DATABASE语句之前必须禁用二进制日志。例如,为了准备迁移数据库而不影响副本,执行以下操作:
-
在单个会话中,禁用二进制日志,并为数据库启用
READ ONLY:mysql> SET sql_log_bin = OFF; mysql> ALTER DATABASE mydb READ ONLY = 1; -
使用mysqldump或mysqlpump等工具对数据库进行转储:
$> mysqldump --databases mydb > mydb.sql -
在单个会话中,禁用二进制日志并禁用数据库的
READ ONLY:mysql> SET sql_log_bin = OFF; mysql> ALTER DATABASE mydb READ ONLY = 0;
15.1.3 ALTER EVENT Statement
ALTER
[DEFINER = *user*]
EVENT *event_name*
[ON SCHEDULE *schedule*]
[ON COMPLETION [NOT] PRESERVE]
[RENAME TO *new_event_name*]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT '*string*']
[DO *event_body*]
ALTER EVENT语句更改现有事件的一个或多个特征,无需删除和重新创建。每个DEFINER、ON SCHEDULE、ON COMPLETION、COMMENT、ENABLE / DISABLE和DO子句的语法与与CREATE EVENT一起使用时完全相同。 (请参见第 15.1.13 节,“CREATE EVENT Statement”。)
任何用户都可以修改在其具有EVENT权限的数据库上定义的事件。当用户执行成功的ALTER EVENT语句时,该用户将成为受影响事件的定义者。
ALTER EVENT仅适用于现有事件:
mysql> ALTER EVENT no_such_event
> ON SCHEDULE
> EVERY '2:3' DAY_HOUR;
ERROR 1517 (HY000): Unknown event 'no_such_event'
在以下每个示例中,假设名为myevent的事件定义如下所示:
CREATE EVENT myevent
ON SCHEDULE
EVERY 6 HOUR
COMMENT 'A sample comment.'
DO
UPDATE myschema.mytable SET mycol = mycol + 1;
以下语句将myevent的计划从立即开始的每六小时一次更改为从运行语句时开始的每十二小时一次,四小时后开始:
ALTER EVENT myevent
ON SCHEDULE
EVERY 12 HOUR
STARTS CURRENT_TIMESTAMP + INTERVAL 4 HOUR;
可以在单个语句中更改事件的多个特征。此示例将myevent执行的 SQL 语句更改为删除mytable中的所有记录;还更改了事件的计划,使其在此ALTER EVENT语句运行后一天执行一次。
ALTER EVENT myevent
ON SCHEDULE
AT CURRENT_TIMESTAMP + INTERVAL 1 DAY
DO
TRUNCATE TABLE myschema.mytable;
仅为要更改的特征在ALTER EVENT语句中指定选项;省略的选项保留其现有值。这包括CREATE EVENT的任何默认值,如ENABLE。
要禁用myevent,请使用此ALTER EVENT语句:
ALTER EVENT myevent
DISABLE;
ON SCHEDULE子句可以使用涉及内置 MySQL 函数和用户变量的表达式来获取其中包含的任何*timestamp或interval*值。您不能在这些表达式中使用存储过程或可加载函数,也不能使用任何表引用;但是,您可以使用SELECT FROM DUAL。这对ALTER EVENT和CREATE EVENT语句都适用。在这种情况下,对存储过程、可加载函数和表的引用是明确不允许的,并且会因错误而失败(请参见 Bug #22830)。
尽管包含另一个ALTER EVENT语句的DO子句的ALTER EVENT语句似乎成功了,但当服务器尝试执行生成的计划事件时,执行会因错误而失败。
要重命名事件,请使用ALTER EVENT语句的RENAME TO子句。此语句将事件myevent重命名为yourevent:
ALTER EVENT myevent
RENAME TO yourevent;
您还可以使用ALTER EVENT ... RENAME TO ...和*db_name.event_name*表示法将事件移动到不同的数据库,如下所示:
ALTER EVENT olddb.myevent
RENAME TO newdb.myevent;
要执行上述语句,执行它的用户必须在olddb和newdb数据库上都具有EVENT权限。
注意
没有RENAME EVENT语句。
值DISABLE ON SLAVE在副本上使用,而不是ENABLE或DISABLE,以指示在复制源服务器上创建并复制到副本的事件,在副本上不执行。通常,DISABLE ON SLAVE会根据需要自动设置;但是,在某些情况下,您可能希望或需要手动更改它。有关更多信息,请参见 Section 19.5.1.16, “Replication of Invoked Features”。
15.1.4 ALTER FUNCTION Statement
ALTER FUNCTION *func_name* [*characteristic* ...]
*characteristic*: {
COMMENT '*string*'
| LANGUAGE SQL
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
}
此语句可用于更改存储函数的特性。ALTER FUNCTION语句中可以指定多个更改。但是,您不能使用此语句更改存储函数的参数或主体;要进行此类更改,必须使用DROP FUNCTION和CREATE FUNCTION删除并重新创建函数。
对于该函数,您必须拥有ALTER ROUTINE权限。(该权限会自动授予函数创建者。)如果启用了二进制日志记录,ALTER FUNCTION语句可能还需要SUPER权限,如第 27.7 节“存储程序二进制日志记录”中所述。
15.1.5 ALTER INSTANCE 语句
ALTER INSTANCE *instance_action*
*instance_action*: {
| {ENABLE|DISABLE} INNODB REDO_LOG
| ROTATE INNODB MASTER KEY
| ROTATE BINLOG MASTER KEY
| RELOAD TLS
[FOR CHANNEL {mysql_main | mysql_admin}]
[NO ROLLBACK ON ERROR]
| RELOAD KEYRING
}
ALTER INSTANCE定义适用于 MySQL 服务器实例的操作。该语句支持以下操作:
-
ALTER INSTANCE {ENABLE | DISABLE} INNODB REDO_LOG此操作启用或禁用
InnoDB重做日志记录。默认情况下启用重做日志记录。此功能仅用于将数据加载到新的 MySQL 实例中。该语句不会写入二进制日志。此操作在 MySQL 8.0.21 中引入。警告
*不要在生产系统上禁用重做日志记录。*虽然允许在禁用重做日志记录时关闭并重新启动服务器,但在禁用重做日志记录时发生意外服务器停止可能会导致数据丢失和实例损坏。
ALTER INSTANCE [ENABLE|DISABLE] INNODB REDO_LOG操作需要独占备份锁,这会阻止其他ALTER INSTANCE操作同时执行。其他ALTER INSTANCE操作必须等待锁被释放后才能执行。更多信息,请参见禁用重做日志记录。
-
ALTER INSTANCE ROTATE INNODB MASTER KEY此操作旋转用于
InnoDB表空间加密的主加密密钥。密钥旋转需要ENCRYPTION_KEY_ADMIN或SUPER权限。要执行此操作,必须安装和配置一个密钥环插件。有关说明,请参见第 8.4.4 节,“MySQL 密钥环”。ALTER INSTANCE ROTATE INNODB MASTER KEY支持并发 DML。但是,它不能与CREATE TABLE ... ENCRYPTION或ALTER TABLE ... ENCRYPTION操作同时运行,并且会获取锁以防止这些语句的并发执行可能引起的冲突。如果其中一个冲突的语句正在运行,则必须等待其完成后才能继续执行另一个。ALTER INSTANCE ROTATE INNODB MASTER KEY语句会写入二进制日志,以便在复制服务器上执行。有关额外的
ALTER INSTANCE ROTATE INNODB MASTER KEY使用信息,请参见第 17.13 节,“InnoDB 数据静止加密”。 -
ALTER INSTANCE ROTATE BINLOG MASTER KEY此操作旋转用于二进制日志加密的二进制日志主密钥。二进制日志主密钥的密钥轮换需要
BINLOG_ENCRYPTION_ADMIN或SUPER权限。如果binlog_encryption系统变量设置为OFF,则不能使用该语句。要执行此操作,必须安装和配置一个密钥环插件。有关说明,请参阅第 8.4.4 节,“MySQL 密钥环”。ALTER INSTANCE ROTATE BINLOG MASTER KEY操作不会写入二进制日志,也不会在副本上执行。因此,二进制日志主密钥轮换可以在包含不同 MySQL 版本的复制环境中进行。要在所有适用的源和副本服务器上安排定期轮换二进制日志主密钥,您可以在每个服务器上启用 MySQL 事件调度程序,并使用CREATE EVENT语句发出ALTER INSTANCE ROTATE BINLOG MASTER KEY语句。如果您因为怀疑当前或任何以前的二进制日志主密钥可能已被泄露而轮换二进制日志主密钥,则在每个适用的源和副本服务器上发出该语句,这样可以验证立即的合规性。有关其他
ALTER INSTANCE ROTATE BINLOG MASTER KEY使用信息,包括如果进程未正确完成或被意外服务器停机中断时该怎么办,请参阅第 19.3.2 节,“加密二进制日志文件和中继日志文件”。 -
ALTER INSTANCE RELOAD TLS这个操作重新配置了 TLS 上下文,使用当前定义上下文的系统变量的值。它还更新了反映活动上下文值的状态变量。此操作需要
CONNECTION_ADMIN权限。有关重新配置 TLS 上下文的其他信息,包括哪些系统和状态变量与上下文相关,请参阅服务器端加密连接的运行时配置和监控。默认情况下,该语句重新加载主连接接口的 TLS 上下文。如果提供了
FOR CHANNEL子句(自 MySQL 8.0.21 起可用),该语句将重新加载命名通道的 TLS 上下文:mysql_main用于主连接接口,mysql_admin用于管理连接接口。有关不同接口的信息,请参见第 7.1.12.1 节,“连接接口”。更新后的 TLS 上下文属性在 Performance Schematls_channel_status表中公开。请参见第 29.12.21.9 节,“tls_channel_status 表”。更新主接口的 TLS 上下文也可能会影响管理接口,因为除非为该接口配置了一些非默认的 TLS 值,否则它将使用与主接口相同的 TLS 上下文。
注意
当重新加载 TLS 上下文时,OpenSSL 会重新加载包含 CRL(证书吊销列表)的文件作为过程的一部分。如果 CRL 文件很大,服务器会分配大块内存(文件大小的十倍),在加载新实例并且旧实例尚未释放时会将其加倍。大量分配被释放后,进程驻留内存不会立即减少,因此如果反复使用带有大型 CRL 文件的
ALTER INSTANCE RELOAD TLS语句,进程驻留内存使用量可能会增加。默认情况下,如果配置值不允许创建新的 TLS 上下文,则
RELOAD TLS操作会回滚并显示错误,不会产生任何效果。先前的上下文值将继续用于新连接。如果给出了可选的NO ROLLBACK ON ERROR子句并且无法创建新上下文,则不会发生回滚。相反,会生成警告,并且对语句适用的接口上的新连接将禁用加密。ALTER INSTANCE RELOAD TLS语句不会写入二进制日志(因此不会被复制)。TLS 配置是本地的,并且依赖于本地文件,不一定存在于所有涉及的服务器上。 -
ALTER INSTANCE RELOAD KEYRING如果安装了密钥环组件,则此操作会告诉组件重新读取其配置文件并重新初始化任何密钥环内存数据。如果您在运行时修改了组件配置,则新配置在执行此操作之前不会生效。重新加载密钥环需要
ENCRYPTION_KEY_ADMIN权限。此操作是在 MySQL 8.0.24 中添加的。此操作仅允许重新配置当前安装的密钥环组件。它不允许更改已安装的组件。例如,如果您更改了已安装的密钥环组件的配置,
ALTER INSTANCE RELOAD KEYRING会使新配置生效。另一方面,如果您更改了服务器清单文件中命名的密钥环组件,ALTER INSTANCE RELOAD KEYRING没有效果,当前组件仍然安装。ALTER INSTANCE RELOAD KEYRING语句不会写入二进制日志(因此不会被复制)。
15.1.6 ALTER LOGFILE GROUP Statement
ALTER LOGFILE GROUP *logfile_group*
ADD UNDOFILE '*file_name*'
[INITIAL_SIZE [=] *size*]
[WAIT]
ENGINE [=] *engine_name*
此语句向现有日志文件组 logfile_group 添加一个名为 'file_name' 的 UNDO 文件。ALTER LOGFILE GROUP 语句只能有一个 ADD UNDOFILE 子句。当前不支持 DROP UNDOFILE 子句。
注意
所有 NDB 集群磁盘数据对象共享相同的命名空间。这意味着每个磁盘数据对象必须具有唯一名称(而不仅仅是给定类型的每个磁盘数据对象)。例如,您不能拥有同名的表空间和撤销日志文件,或者同名的撤销日志文件和数据文件。
可选的 INITIAL_SIZE 参数设置 UNDO 文件的初始大小(以字节为单位);如果未指定,初始大小默认为 134217728(128 MB)。您可以选择在 size 后跟一个表示数量级的字母缩写,类似于 my.cnf 中使用的缩写。通常,这是 M(兆字节)或 G(千兆字节)中的一个字母。 (Bug #13116514, Bug #16104705, Bug #62858)
在 32 位系统上,INITIAL_SIZE 的最大支持值为 4294967296(4 GB)。 (Bug #29186)
INITIAL_SIZE 的最小允许值为 1048576(1 MB)。 (Bug #29574)
注意
WAIT 被解析但被忽略。此关键字目前没有任何效果,预计用于未来扩展。
ENGINE 参数(必需)确定此日志文件组使用的存储引擎,engine_name 是存储引擎的名称。目前,engine_name 的唯一接受值为“NDBCLUSTER”和“NDB”。这两个值是等效的。
这里有一个示例,假设日志文件组 lg_3 已经使用 CREATE LOGFILE GROUP 创建(参见 Section 15.1.16, “CREATE LOGFILE GROUP Statement”):
ALTER LOGFILE GROUP lg_3
ADD UNDOFILE 'undo_10.dat'
INITIAL_SIZE=32M
ENGINE=NDBCLUSTER;
当使用 ALTER LOGFILE GROUP 与 ENGINE = NDBCLUSTER(或者 ENGINE = NDB)时,在每个 NDB 集群数据节点上创建一个 UNDO 日志文件。您可以通过查询信息模式 FILES 表来验证 UNDO 文件是否已创建并获取有关它们的信息。例如:
mysql> SELECT FILE_NAME, LOGFILE_GROUP_NUMBER, EXTRA
-> FROM INFORMATION_SCHEMA.FILES
-> WHERE LOGFILE_GROUP_NAME = 'lg_3';
+-------------+----------------------+----------------+
| FILE_NAME | LOGFILE_GROUP_NUMBER | EXTRA |
+-------------+----------------------+----------------+
| newdata.dat | 0 | CLUSTER_NODE=3 |
| newdata.dat | 0 | CLUSTER_NODE=4 |
| undo_10.dat | 11 | CLUSTER_NODE=3 |
| undo_10.dat | 11 | CLUSTER_NODE=4 |
+-------------+----------------------+----------------+
4 rows in set (0.01 sec)
(参见 Section 28.3.15, “The INFORMATION_SCHEMA FILES Table”.)
内存用于UNDO_BUFFER_SIZE来自全局池,其大小由SharedGlobalMemory数据节点配置参数的值确定。这包括由InitialLogFileGroup数据节点配置参数的设置隐含的任何默认值。
ALTER LOGFILE GROUP仅适用于 NDB Cluster 的磁盘数据存储。更多信息,请参见 Section 25.6.11, “NDB Cluster Disk Data Tables”。
15.1.7 ALTER PROCEDURE 语句
ALTER PROCEDURE *proc_name* [*characteristic* ...]
*characteristic*: {
COMMENT '*string*'
| LANGUAGE SQL
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
}
这个语句可以用来改变存储过程的特性。一个ALTER PROCEDURE语句中可以指定多个更改。然而,你不能使用这个语句来改变存储过程的参数或主体;要进行这样的更改,你必须使用DROP PROCEDURE和CREATE PROCEDURE来删除并重新创建该存储过程。
你必须拥有ALTER ROUTINE权限才能操作该存储过程。默认情况下,该权限会自动授予给存储过程的创建者。可以通过禁用automatic_sp_privileges系统变量来改变这种行为。参见 Section 27.2.2, “Stored Routines and MySQL Privileges”。
15.1.8 ALTER SERVER 语句
ALTER SERVER *server_name*
OPTIONS (*option* [, *option*] ...)
改变*server_name*的服务器信息,调整CREATE SERVER语句中允许的任何选项。相应的mysql.servers表中的字段将相应更新。此语句需要SUPER权限。
例如,要更新USER选项:
ALTER SERVER s OPTIONS (USER 'sally');
ALTER SERVER会导致隐式提交。参见 Section 15.3.3, “Statements That Cause an Implicit Commit”。
无论使用的日志格式如何,ALTER SERVER都不会被写入二进制日志。
15.1.9 ALTER TABLE 语句
15.1.9.1 ALTER TABLE 分区操作
15.1.9.2 ALTER TABLE 和生成列
15.1.9.3 ALTER TABLE 示例
ALTER TABLE *tbl_name*
[*alter_option* [, *alter_option*] ...]
[*partition_options*]
*alter_option*: {
*table_options*
| ADD [COLUMN] *col_name* *column_definition*
[FIRST | AFTER *col_name*]
| ADD [COLUMN] (*col_name* *column_definition*,...)
| ADD {INDEX | KEY} [*index_name*]
[*index_type*] (*key_part*,...) [*index_option*] ...
| ADD {FULLTEXT | SPATIAL} [INDEX | KEY] [*index_name*]
(*key_part*,...) [*index_option*] ...
| ADD [CONSTRAINT [*symbol*]] PRIMARY KEY
[*index_type*] (*key_part*,...)
[*index_option*] ...
| ADD [CONSTRAINT [*symbol*]] UNIQUE [INDEX | KEY]
[*index_name*] [*index_type*] (*key_part*,...)
[*index_option*] ...
| ADD [CONSTRAINT [*symbol*]] FOREIGN KEY
[*index_name*] (*col_name*,...)
*reference_definition*
| ADD [CONSTRAINT [*symbol*]] CHECK (*expr*) [[NOT] ENFORCED]
| DROP {CHECK | CONSTRAINT} *symbol*
| ALTER {CHECK | CONSTRAINT} *symbol* [NOT] ENFORCED
| ALGORITHM [=] {DEFAULT | INSTANT | INPLACE | COPY}
| ALTER [COLUMN] *col_name* {
SET DEFAULT {*literal* | (*expr*)}
| SET {VISIBLE | INVISIBLE}
| DROP DEFAULT
}
| ALTER INDEX *index_name* {VISIBLE | INVISIBLE}
| CHANGE [COLUMN] *old_col_name* *new_col_name* *column_definition*
[FIRST | AFTER *col_name*]
| [DEFAULT] CHARACTER SET [=] *charset_name* [COLLATE [=] *collation_name*]
| CONVERT TO CHARACTER SET *charset_name* [COLLATE *collation_name*]
| {DISABLE | ENABLE} KEYS
| {DISCARD | IMPORT} TABLESPACE
| DROP [COLUMN] *col_name*
| DROP {INDEX | KEY} *index_name*
| DROP PRIMARY KEY
| DROP FOREIGN KEY *fk_symbol*
| FORCE
| LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}
| MODIFY [COLUMN] *col_name* *column_definition*
[FIRST | AFTER *col_name*]
| ORDER BY *col_name* [, *col_name*] ...
| RENAME COLUMN *old_col_name* TO *new_col_name*
| RENAME {INDEX | KEY} *old_index_name* TO *new_index_name*
| RENAME [TO | AS] *new_tbl_name*
| {WITHOUT | WITH} VALIDATION
}
*partition_options*:
*partition_option* [*partition_option*] ...
*partition_option*: {
ADD PARTITION (*partition_definition*)
| DROP PARTITION *partition_names*
| DISCARD PARTITION {*partition_names* | ALL} TABLESPACE
| IMPORT PARTITION {*partition_names* | ALL} TABLESPACE
| TRUNCATE PARTITION {*partition_names* | ALL}
| COALESCE PARTITION *number*
| REORGANIZE PARTITION *partition_names* INTO (*partition_definitions*)
| EXCHANGE PARTITION *partition_name* WITH TABLE *tbl_name* [{WITH | WITHOUT} VALIDATION]
| ANALYZE PARTITION {*partition_names* | ALL}
| CHECK PARTITION {*partition_names* | ALL}
| OPTIMIZE PARTITION {*partition_names* | ALL}
| REBUILD PARTITION {*partition_names* | ALL}
| REPAIR PARTITION {*partition_names* | ALL}
| REMOVE PARTITIONING
}
*key_part*: {*col_name* [(*length*)] | (*expr*)} [ASC | DESC]
*index_type*:
USING {BTREE | HASH}
*index_option*: {
KEY_BLOCK_SIZE [=] *value*
| *index_type*
| WITH PARSER *parser_name*
| COMMENT '*string*'
| {VISIBLE | INVISIBLE}
}
*table_options*:
*table_option* [[,] *table_option*] ...
*table_option*: {
AUTOEXTEND_SIZE [=] *value*
| AUTO_INCREMENT [=] *value*
| AVG_ROW_LENGTH [=] *value*
| [DEFAULT] CHARACTER SET [=] *charset_name*
| CHECKSUM [=] {0 | 1}
| [DEFAULT] COLLATE [=] *collation_name*
| COMMENT [=] '*string*'
| COMPRESSION [=] {'ZLIB' | 'LZ4' | 'NONE'}
| CONNECTION [=] '*connect_string*'
| {DATA | INDEX} DIRECTORY [=] '*absolute path to directory*'
| DELAY_KEY_WRITE [=] {0 | 1}
| ENCRYPTION [=] {'Y' | 'N'}
| ENGINE [=] *engine_name*
| ENGINE_ATTRIBUTE [=] '*string*'
| INSERT_METHOD [=] { NO | FIRST | LAST }
| KEY_BLOCK_SIZE [=] *value*
| MAX_ROWS [=] *value*
| MIN_ROWS [=] *value*
| PACK_KEYS [=] {0 | 1 | DEFAULT}
| PASSWORD [=] '*string*'
| ROW_FORMAT [=] {DEFAULT | DYNAMIC | FIXED | COMPRESSED | REDUNDANT | COMPACT}
| SECONDARY_ENGINE_ATTRIBUTE [=] '*string*'
| STATS_AUTO_RECALC [=] {DEFAULT | 0 | 1}
| STATS_PERSISTENT [=] {DEFAULT | 0 | 1}
| STATS_SAMPLE_PAGES [=] *value*
| TABLESPACE *tablespace_name* [STORAGE {DISK | MEMORY}]
| UNION [=] (*tbl_name*[,*tbl_name*]...)
}
*partition_options*:
(see CREATE TABLE options)
ALTER TABLE 改变表的结构。例如,您可以添加或删除列,创建或销毁索引,更改现有列的类型,或重命名列或表本身。您还可以更改诸如用于表的存储引擎或表注释等特性。
-
要使用
ALTER TABLE,您需要对表具有ALTER、CREATE和INSERT权限。重命名表需要对旧表具有ALTER和DROP权限,对新表具有ALTER、CREATE和INSERT权限。 -
在表名之后,指定要进行的更改。如果没有给出任何更改,则
ALTER TABLE不执行任何操作。 -
许多允许的更改的语法与
CREATE TABLE语句的子句类似。column_definition子句对于ADD和CHANGE使用与CREATE TABLE相同的语法。有关更多信息,请参见 Section 15.1.20, “CREATE TABLE Statement”。 -
COLUMN这个词是可选的,可以省略,除了RENAME COLUMN(用于区分列重命名操作和表重命名操作)。 -
在单个
ALTER TABLE语句中允许多个ADD、ALTER、DROP和CHANGE子句,用逗号分隔。这是 MySQL 对标准 SQL 的扩展,标准 SQL 每个ALTER TABLE语句只允许每个子句中的一个。例如,要在单个语句中删除多个列,请执行以下操作:ALTER TABLE t2 DROP COLUMN c, DROP COLUMN d; -
如果存储引擎不支持尝试的
ALTER TABLE操作,则可能会产生警告。此类警告可以使用SHOW WARNINGS显示。请参阅第 15.7.7.42 节,“SHOW WARNINGS 语句”。有关故障排除ALTER TABLE的信息,请参阅附录 B.3.6.1,“ALTER TABLE 问题”。 -
有关生成列的信息,请参阅第 15.1.9.2 节,“ALTER TABLE 和生成列”。
-
有关用法示例,请参阅第 15.1.9.3 节,“ALTER TABLE 示例”。
-
在 MySQL 8.0.17 及更高版本中,
InnoDB支持对 JSON 列添加多值索引,使用*key_part*规范可以采用(CAST *json_path* AS *type* ARRAY)的形式。请参阅多值索引,了解有关多值索引创建和使用的详细信息,以及多值索引的限制和限制。 -
使用
mysql_info()C API 函数,您可以查找由ALTER TABLE复制了多少行。请参阅 mysql_info()。
ALTER TABLE语句还有几个额外方面,在本节中的以下主题下进行了描述:
-
表选项
-
性能和空间要求
-
并发控制
-
添加和删除列
-
重命名、重新定义和重新排序列
-
主键和索引
-
外键和其他约束
-
更改字符集
-
导入 InnoDB 表
-
MyISAM 表的行顺序
-
分区选项
表选项
*table_options*表示可以在CREATE TABLE语句中使用的表选项,例如ENGINE、AUTO_INCREMENT、AVG_ROW_LENGTH、MAX_ROWS、ROW_FORMAT或TABLESPACE。
有关所有表选项的描述,请参见 Section 15.1.20, “CREATE TABLE Statement”。但是,当作为表选项给出时,ALTER TABLE会忽略DATA DIRECTORY和INDEX DIRECTORY。ALTER TABLE仅允许它们作为分区选项,并要求您具有FILE权限。
使用ALTER TABLE的表选项提供了一种方便的方式来更改单个表的特性。例如:
-
如果
t1当前不是InnoDB表,则此语句将其存储引擎更改为InnoDB。ALTER TABLE t1 ENGINE = InnoDB;-
有关将表切换到
InnoDB存储引擎时的注意事项,请参见 Section 17.6.1.5, “Converting Tables from MyISAM to InnoDB”。 -
当指定
ENGINE子句时,ALTER TABLE会重建表。即使表已经具有指定的存储引擎,这也是正确的。 -
在现有的
InnoDB表上运行ALTER TABLE *tbl_name* ENGINE=INNODB执行一个“null”ALTER TABLE操作,可用于碎片整理InnoDB表,如 Section 17.11.4, “Defragmenting a Table”中所述。在InnoDB表上运行ALTER TABLE *tbl_name* FORCE执行相同的功能。 -
ALTER TABLE *tbl_name* ENGINE=INNODB和ALTER TABLE *tbl_name* FORCE使用在线 DDL。有关更多信息,请参见 Section 17.12, “InnoDB and Online DDL”。 -
尝试更改表的存储引擎的结果受所需存储引擎是否可用以及
NO_ENGINE_SUBSTITUTIONSQL 模式设置的影响,如 Section 7.1.11, “Server SQL Modes”中所述。 -
为防止数据意外丢失,
ALTER TABLE不能用于将表的存储引擎更改为MERGE或BLACKHOLE。
-
-
要将
InnoDB表更改为使用压缩行存储格式:ALTER TABLE t1 ROW_FORMAT = COMPRESSED; -
ENCRYPTION子句为InnoDB表启用或禁用页面级数据加密。必须安装和配置一个密钥环插件才能启用加密。如果启用了
table_encryption_privilege_check变量,则需要TABLE_ENCRYPTION_ADMIN权限才能使用与默认模式加密设置不同的设置的ENCRYPTION子句。在 MySQL 8.0.16 之前,
ENCRYPTION子句仅在更改位于每表一个文件表空间中的表时受支持。从 MySQL 8.0.16 开始,ENCRYPTION子句也支持位于通用表空间中的表。对于位于通用表空间中的表,表和表空间加密必须匹配。
不允许通过将表移动到不同的表空间或更改存储引擎来更改表加密,而不明确指定
ENCRYPTION子句。从 MySQL 8.0.16 开始,如果表使用不支持加密的存储引擎,则不允许使用值为
'N'或''的ENCRYPTION子句。以前,该子句是被接受的。在使用不支持加密的存储引擎在启用加密的模式中创建不带ENCRYPTION子句的表也是不允许的。欲了解更多信息,请参阅第 17.13 节,“InnoDB 数据静态加密”。
-
要重置当前自增值:
ALTER TABLE t1 AUTO_INCREMENT = 13;不能将计数器重置为小于或等于当前正在使用的值。对于
InnoDB和MyISAM,如果该值小于或等于当前在AUTO_INCREMENT列中的最大值,则该值将重置为当前最大AUTO_INCREMENT列值加一。 -
要更改默认表字符集:
ALTER TABLE t1 CHARACTER SET = utf8mb4;另请参阅更改字符集。
-
要添加(或更改)表注释:
ALTER TABLE t1 COMMENT = 'New table comment'; -
使用带有
TABLESPACE选项的ALTER TABLE来在现有通用表空间、每表一个文件表空间和系统表空间之间移动InnoDB表。参见使用 ALTER TABLE 在表空间之间移动表。-
ALTER TABLE ... TABLESPACE操作总是导致完整的表重建,即使TABLESPACE属性未从其先前值更改。 -
ALTER TABLE ... TABLESPACE语法不支持将表从临时表空间移动到持久表空间。 -
支持与
CREATE TABLE ... TABLESPACE一起使用的DATA DIRECTORY子句,在ALTER TABLE ... TABLESPACE中不受支持,并且如果指定了将被忽略。 -
有关
TABLESPACE选项的功能和限制的更多信息,请参见CREATE TABLE。
-
-
MySQL NDB Cluster 8.0 支持设置
NDB_TABLE选项,用于控制表的分区平衡(片段计数类型)、从任何副本读取的能力、完全复制,或这些选项的任意组合,作为ALTER TABLE语句的表注释的一部分,与CREATE TABLE一样,如下例所示:ALTER TABLE t1 COMMENT = "NDB_TABLE=READ_BACKUP=0,PARTITION_BALANCE=FOR_RA_BY_NODE";还可以在
ALTER TABLE语句中为NDB表的列设置NDB_COMMENT选项,如下所示:ALTER TABLE t1 CHANGE COLUMN c1 c1 BLOB COMMENT = 'NDB_COLUMN=BLOB_INLINE_SIZE=4096,MAX_BLOB_PART_SIZE';通过 NDB 8.0.30 及更高版本支持以这种方式设置 blob 内联大小。请注意,
ALTER TABLE ... COMMENT ...会丢弃表的任何现有注释。有关更多信息和示例,请参见设置 NDB_TABLE 选项。 -
ENGINE_ATTRIBUTE和SECONDARY_ENGINE_ATTRIBUTE选项(自 MySQL 8.0.21 起可用)用于指定主要和次要存储引擎的表、列和索引属性。这些选项保留供将来使用。索引属性无法更改。必须删除索引并以所需更改重新添加,这可以在单个ALTER TABLE语句中执行。
要验证表选项是否按预期更改,请使用SHOW CREATE TABLE,或查询信息模式TABLES表。
性能和空间要求
ALTER TABLE操作使用以下算法之一进行处理:
-
COPY: 操作在原始表的副本上执行,并且表数据逐行从原始表复制到新表。不允许并发的 DML。 -
INPLACE: 操作避免复制表数据,但可能会就地重建表。在操作的准备和执行阶段可能会短暂地对表进行独占的元数据锁定。通常支持并发的 DML。 -
INSTANT: 操作仅修改数据字典中的元数据。在操作的执行阶段可能会短暂地对表进行独占的元数据锁定。表数据不受影响,使操作瞬间完成。允许并发的 DML。(MySQL 8.0.12 中引入)
对于使用NDB存储引擎的表,这些算法的工作方式如下:
-
COPY:NDB创建表的副本并对其进行更改;然后 NDB Cluster 处理程序在旧表和新表之间复制数据。随后,NDB删除旧表并重命名新表。有时也被称为“复制”或“离线”
ALTER TABLE。 -
INPLACE:数据节点进行所需更改;NDB Cluster 处理程序不复制数据或以其他方式参与。有时也被称为“非复制”或“在线”
ALTER TABLE。 -
INSTANT:不被NDB支持。
更多信息请参见 Section 25.6.12, “NDB Cluster 中的 ALTER TABLE 在线操作”。
ALGORITHM子句是可选的。如果省略了ALGORITHM子句,MySQL 将对支持它的存储引擎和ALTER TABLE子句使用ALGORITHM=INSTANT。否则,将使用ALGORITHM=INPLACE。如果不支持ALGORITHM=INPLACE,则使用ALGORITHM=COPY。
注意
使用ALGORITHM=INSTANT向分区表添加列后,不再可以对表执行ALTER TABLE ... EXCHANGE PARTITION操作。
指定ALGORITHM子句要求操作使用指定的算法对支持它的子句和存储引擎,否则将失败并显示错误。指定ALGORITHM=DEFAULT与省略ALGORITHM子句相同。
使用COPY算法的ALTER TABLE操作会等待正在修改表的其他操作完成。在对表副本应用更改后,数据被复制过去,原始表被删除,表副本被重命名为原始表的名称。在ALTER TABLE操作执行时,原始表可以被其他会话读取(除了不久前提到的例外)。在ALTER TABLE操作开始后对表的更新和写入被暂停,直到新表准备就绪,然后自动重定向到新表。表的临时副本被创建在原始表的数据库目录中,除非是将表移动到不同目录中的数据库的RENAME TO操作。
先前提到的异常是,ALTER TABLE在准备清除过时表结构时会阻止读取(不仅仅是写入)。在这一点上,它必须获取独占锁。为此,它等待当前读取器完成,并阻止新的读取和写入。
使用COPY算法的ALTER TABLE操作会阻止并发的 DML 操作。仍然允许并发查询。也就是说,表复制操作始终至少包括LOCK=SHARED的并发限制(允许查询但不允许 DML)。您可以通过指定LOCK=EXCLUSIVE进一步限制支持LOCK子句的操作的并发性,从而阻止 DML 和查询。有关更多信息,请参阅并发控制。
要强制使用COPY算法进行本来不使用的ALTER TABLE操作,指定ALGORITHM=COPY或启用old_alter_table系统变量。如果old_alter_table设置与具有非DEFAULT值的ALGORITHM子句之间存在冲突,则ALGORITHM子句优先。
对于InnoDB表,使用COPY算法进行ALTER TABLE操作的表位于共享表空间中,可能会增加表空间使用量。这种操作需要额外的空间,与表中的数据加索引一样多。对于位于共享表空间中的表,在操作期间使用的额外空间不会像位于 file-per-table 表空间中的表那样释放回操作系统。
有关在线 DDL 操作的空间要求,请参阅 Section 17.12.3, “Online DDL Space Requirements”。
支持INPLACE算法的ALTER TABLE操作包括:
-
InnoDB支持的ALTER TABLE操作在线 DDL 功能。请参阅 Section 17.12.1, “Online DDL Operations”。 -
重命名表。MySQL 会重命名与表*
tbl_name*对应的文件而不进行复制。(您也可以使用RENAME TABLE语句来重命名表。请参见第 15.1.36 节,“RENAME TABLE Statement”。)专门授予重命名表的权限不会迁移到新名称。必须手动更改它们。 -
仅修改表元数据的操作。这些操作是立即执行的,因为服务器不会触及表内容。仅元数据操作包括:
-
重命名列。在 NDB Cluster 8.0.18 及更高版本中,此操作也可以在线执行。
-
更改列的默认值(除了
NDB表)。 -
通过在有效成员值列表的末尾添加新的枚举或集合成员来修改
ENUM或SET列的定义,只要数据类型的存储大小不变。例如,在具有 8 个成员的SET列中添加一个成员会将每个值所需的存储从 1 字节更改为 2 字节;这需要复制表。在列表中间添加成员会导致现有成员的重新编号,这需要复制表。 -
更改空间列的定义以删除
SRID属性。(添加或更改SRID属性需要重建,不能就地完成,因为服务器必须验证所有值是否具有指定的SRID值。) -
截至 MySQL 8.0.14,更改列字符集的条件如下:
-
列数据类型为
CHAR、VARCHAR、TEXT类型或ENUM。 -
字符集从
utf8mb3更改为utf8mb4,或任何字符集更改为binary。 -
列上没有索引。
-
-
截至 MySQL 8.0.14,更改生成列的条件如下:
-
对于
InnoDB表,修改生成的存储列但不更改其类型、表达式或可空性的语句。 -
对于非
InnoDB表,修改生成的存储或虚拟列但不更改其类型、表达式或可空性的语句。
更改列注释的示例。
-
-
-
重命名索引。
-
为
InnoDB和NDB表添加或删除辅助索引。请参见第 17.12.1 节,“在线 DDL 操作”。 -
对于
NDB表,对可变宽度列添加和删除索引的操作。这些操作在线进行,无需复制表格,并且在大部分时间内不会阻塞并发的 DML 操作。请参见第 25.6.12 节,“NDB Cluster 中 ALTER TABLE 的在线操作”。 -
使用
ALTER INDEX操作修改索引可见性。 -
修改包含依赖于具有
DEFAULT值的列的生成列的表的列。例如,可以在不重建表格的情况下进行单独列的NULL属性更改。
支持INSTANT算法的ALTER TABLE操作包括:
-
添加列。此功能称为“即时
ADD COLUMN”。有限制条件。请参见第 17.12.1 节,“在线 DDL 操作”。 -
删除列。此功能称为“即时
DROP COLUMN”。有限制条件。请参见第 17.12.1 节,“在线 DDL 操作”。 -
添加或删除虚拟列。
-
添加或删除列默认值。
-
修改
ENUM或SET列的定义。与上述描述的ALGORITHM=INSTANT相同的限制条件适用。 -
更改索引类型。
-
重命名表。与上述描述的
ALGORITHM=INSTANT相同的限制条件适用。
有关支持ALGORITHM=INSTANT的操作的更多信息,请参见第 17.12.1 节,“在线 DDL 操作”。
ALTER TABLE将 MySQL 5.5 的时间列升级为 5.6 格式,用于ADD COLUMN、CHANGE COLUMN、MODIFY COLUMN、ADD INDEX和FORCE操作。此转换不能使用INPLACE算法进行,因为必须重建表格,因此在这些情况下指定ALGORITHM=INPLACE会导致错误。如有必要,请指定ALGORITHM=COPY。
如果对通过KEY对表进行分区的多列索引进行的ALTER TABLE操作改变了列的顺序,则只能使用ALGORITHM=COPY执行。
WITHOUT VALIDATION和WITH VALIDATION子句影响ALTER TABLE对虚拟生成列修改的是否进行原地操作。请参见第 15.1.9.2 节,“ALTER TABLE 和生成列”。
NDB Cluster 8.0 支持使用与标准 MySQL Server 相同的ALGORITHM=INPLACE语法进行在线操作。NDB不支持在线更改表空间;从 NDB 8.0.21 开始,不允许这样做。有关更多信息,请参见第 25.6.12 节,“NDB Cluster 中的 ALTER TABLE 在线操作”。
在 NDB 8.0.27 及更高版本中,执行复制ALTER TABLE时,会检查确保没有对受影响表进行并发写入。如果发现有任何并发写入,NDB会拒绝ALTER TABLE语句并引发ER_TABLE_DEF_CHANGED。
使用DISCARD ... PARTITION ... TABLESPACE或IMPORT ... PARTITION ... TABLESPACE的ALTER TABLE不会创建任何临时表或临时分区文件。
使用ADD PARTITION、DROP PARTITION、COALESCE PARTITION、REBUILD PARTITION或REORGANIZE PARTITION的ALTER TABLE不会创建临时表(除非与NDB表一起使用);但是,这些操作可以并且会创建临时分区文件。
RANGE或LIST分区的ADD或DROP操作是立即操作或几乎是立即操作。HASH或KEY分区的ADD或COALESCE操作会在所有分区之间复制数据,除非使用了LINEAR HASH或LINEAR KEY;这实际上等同于创建一个新表,尽管ADD或COALESCE操作是逐个分区执行的。REORGANIZE操作只复制已更改的分区,不会触及未更改的分区。
对于MyISAM表,可以通过将myisam_sort_buffer_size系统变量设置为较高的值来加快索引重建(修改过程中最慢的部分)。
并发控制
对于支持的ALTER TABLE操作,可以使用LOCK子句来控制在修改表时对表进行并发读写的级别。为此子句指定非默认值可以要求在修改操作期间具有一定程度的并发访问或独占性,并且如果所请求的锁定程度不可用,则会停止操作。
仅允许对使用ALGORITHM=INSTANT的操作使用LOCK = DEFAULT。其他LOCK子句参数不适用。
LOCK子句的参数为:
-
LOCK = DEFAULT给定
ALGORITHM子句(如果有)和ALTER TABLE操作的最大并发级别:如果支持,允许并发读写。如果不支持,则允许并发读取。如果不支持,则强制独占访问。 -
LOCK = NONE如果支持,允许并发读写。否则,将发生错误。
-
LOCK = SHARED如果支持,允许并发读取但阻止写入。即使存储引擎支持给定
ALGORITHM子句(如果有)和ALTER TABLE操作的并发写入,写入也会被阻止。如果不支持并发读取,则会发生错误。 -
LOCK = EXCLUSIVE强制独占访问。即使存储引擎支持给定
ALGORITHM子句(如果有)和ALTER TABLE操作的并发读/写,也会执行此操作。
添加和删除列
使用ADD来向表中添加新列,使用DROP来移除现有列。DROP *col_name*是 MySQL 对标准 SQL 的扩展。
要在表行中的特定位置添加列,请使用FIRST或AFTER *col_name*。默认情况下,将列添加到最后。
如果表只包含一列,则无法删除该列。如果您打算删除表,请改用DROP TABLE语句。
如果从表中删除列,则这些列也将从它们所属的任何索引中删除。如果组成索引的所有列都被删除,则索引也将被删除。如果使用CHANGE或MODIFY来缩短具有索引的列,并且结果列长度小于索引长度,则 MySQL 会自动缩短索引。
对于ALTER TABLE ... ADD,如果列具有使用非确定性函数的表达式默认值,该语句可能会产生警告或错误。有关更多信息,请参见第 13.6 节,“数据类型默认值”和第 19.1.3.7 节,“带有 GTID 的复制限制”。
重命名、重新定义和重新排序列
CHANGE,MODIFY,RENAME COLUMN和ALTER子句使得可以更改现有列的名称和定义。它们具有以下比较特性:
-
CHANGE:-
可以重命名列并更改其定义,或两者兼而有之。
-
拥有比
MODIFY或RENAME COLUMN更多的功能,但某些操作的便利性受到牺牲。如果不重命名,CHANGE需要两次命名列,并且如果只是重命名,则需要重新指定列定义。 -
使用
FIRST或AFTER可以重新排序列。
-
-
MODIFY:-
可以更改列定义但不能更改其名称。
-
比
CHANGE更方便,可以更改列定义而不重命名。 -
使用
FIRST或AFTER可以重新排序列。
-
-
RENAME COLUMN:-
可以更改列名但不能更改其定义。
-
比
CHANGE更方便,可以重命名列而不改变其定义。
-
-
ALTER:仅用于更改列的默认值。
CHANGE是 MySQL 对标准 SQL 的扩展。MODIFY和RENAME COLUMN是为了 Oracle 兼容性而添加的 MySQL 扩展。
要更改列的名称和定义,请使用CHANGE,指定旧名称和新名称以及新定义。例如,要将INT NOT NULL列从a重命名为b并将其定义更改为使用BIGINT数据类型同时保留NOT NULL属性,请执行以下操作:
ALTER TABLE t1 CHANGE a b BIGINT NOT NULL;
要更改列定义但不更改其名称,请使用CHANGE或MODIFY。使用CHANGE,语法要求两个列名,因此您必须指定相同的名称两次以保持名称不变。例如,要更改列b的定义,请执行以下操作:
ALTER TABLE t1 CHANGE b b INT NOT NULL;
MODIFY更方便地更改定义而不更改名称,因为它只需要列名一次:
ALTER TABLE t1 MODIFY b INT NOT NULL;
要更改列名称但不更改其定义,请使用CHANGE或RENAME COLUMN。使用CHANGE,语法要求列定义,因此要保持定义不变,您必须重新指定列当前具有的定义。例如,要将INT NOT NULL列从b重命名为a,请执行以下操作:
ALTER TABLE t1 CHANGE b a INT NOT NULL;
使用RENAME COLUMN更方便地更改名称而不更改定义,因为它只需要旧名称和新名称:
ALTER TABLE t1 RENAME COLUMN b TO a;
通常,您不能将列重命名为表中已存在的名称。但是,有时情况并非如此,例如当您交换名称或通过循环移动它们时。如果表中有列名为a、b和c,则以下操作是有效的:
-- swap a and b
ALTER TABLE t1 RENAME COLUMN a TO b,
RENAME COLUMN b TO a;
-- "rotate" a, b, c through a cycle
ALTER TABLE t1 RENAME COLUMN a TO b,
RENAME COLUMN b TO c,
RENAME COLUMN c TO a;
对于使用CHANGE或MODIFY进行列定义更改,定义必须包括数据类型和应适用于新列的所有属性,除了索引属性如PRIMARY KEY或UNIQUE之外。原始定义中存在但未为新定义指定的属性不会被继承。假设列col1被定义为INT UNSIGNED DEFAULT 1 COMMENT 'my column',并且您修改列如下,意图仅将INT更改为BIGINT:
ALTER TABLE t1 MODIFY col1 BIGINT;
该语句将数据类型从INT更改为BIGINT,但也会删除UNSIGNED、DEFAULT和COMMENT属性。要保留它们,语句必须显式包含它们:
ALTER TABLE t1 MODIFY col1 BIGINT UNSIGNED DEFAULT 1 COMMENT 'my column';
对于使用CHANGE或MODIFY进行数据类型更改,MySQL 会尽可能将现有列值转换为新类型。
警告
此转换可能导致数据的更改。例如,如果缩短字符串列,值可能会被截断。为防止如果转换为新数据类型会导致数据丢失,则在使用ALTER TABLE之前启用严格的 SQL 模式(请参阅 Section 7.1.11, “Server SQL Modes”)。
如果您使用CHANGE或MODIFY缩短具有索引的列,并且结果列长度小于索引长度,则 MySQL 会自动缩短索引。
对于由CHANGE或RENAME COLUMN重命名的列,MySQL 会自动重命名对重命名列的引用:
-
引用旧列的索引,包括不可见索引和禁用的
MyISAM索引。 -
外键引用旧列。
对于被CHANGE或RENAME COLUMN重命名的列,MySQL 不会自动重命名这些引用到重命名列的引用:
-
引用重命名列的生成列和分区表达式。您必须在同一
ALTER TABLE语句中使用CHANGE重新定义这样的表达式,以重命名列。 -
引用重命名列的视图和存储程序。您必须手动修改这些对象的定义,以引用新的列名。
要在表内重新排序列,请在CHANGE或MODIFY操作中使用FIRST和AFTER。
ALTER ... SET DEFAULT 或 ALTER ... DROP DEFAULT 分别指定列的新默认值或移除旧的默认值。如果移除了旧的默认值并且列可以为NULL,新的默认值将为NULL。如果列不能为NULL,MySQL 将分配一个默认值,如第 13.6 节,“数据类型默认值”中所述。
截至 MySQL 8.0.23,ALTER ... SET VISIBLE 和 ALTER ... SET INVISIBLE 允许更改列的可见性。请参见第 15.1.20.10 节,“不可见列”。
主键和索引
DROP PRIMARY KEY 删除主键。如果没有主键,将会出现错误。关于主键的性能特性的信息,尤其是对于InnoDB表,参见第 10.3.2 节,“主键优化”。
如果启用了sql_require_primary_key系统变量,尝试删除主键将产生错误。
如果向表中添加UNIQUE INDEX或PRIMARY KEY,MySQL 会将其存储在任何非唯一索引之前,以便尽早检测到重复键。
DROP INDEX 删除一个索引。这是 MySQL 对标准 SQL 的扩展。请参见第 15.1.27 节,“DROP INDEX 语句”。要确定索引名称,请使用SHOW INDEX FROM *tbl_name*。
一些存储引擎允许在创建索引时指定索引类型。*index_type*指定符的语法为USING *type_name*。有关USING的详细信息,请参见第 15.1.15 节,“CREATE INDEX 语句”。首选位置是在列列表之后。预计在未来的 MySQL 版本中将删除在列列表之前使用该选项的支持。
*index_option值指定索引的附加选项。USING是其中之一。有关允许的index_option*值的详细信息,请参见第 15.1.15 节,“CREATE INDEX Statement”。
RENAME INDEX *old_index_name* TO *new_index_name* 重命名索引。这是 MySQL 对标准 SQL 的扩展。表的内容保持不变。*old_index_name必须是表中现有索引的名称,该索引不会被相同的ALTER TABLE语句删除。new_index_name*是新的索引名称,在应用更改后的表中不能重复索引的名称。两个索引名称都不能是PRIMARY。
如果在MyISAM表上使用ALTER TABLE,所有非唯一索引将在单独的批处理中创建(就像REPAIR TABLE一样)。当有许多索引时,这应该使ALTER TABLE速度更快。
对于MyISAM表,可以显式控制键的更新。使用ALTER TABLE ... DISABLE KEYS告诉 MySQL 停止更新非唯一索引。然后使用ALTER TABLE ... ENABLE KEYS重新创建丢失的索引。MyISAM使用一种比逐个插入键更快的特殊算法来执行此操作,因此在执行大量插入操作之前禁用键应该会显著加快速度。使用ALTER TABLE ... DISABLE KEYS需要除了前面提到的权限之外的INDEX权限。
在非唯一索引被禁用时,它们对于诸如SELECT和EXPLAIN等通常会使用它们的语句将被忽略。
在ALTER TABLE语句之后,可能需要运行ANALYZE TABLE来更新索引基数信息。参见第 15.7.7.22 节,“SHOW INDEX Statement”。
ALTER INDEX操作允许将索引设置为可见或不可见。不可见索引不会被优化器使用。索引可见性的修改适用于主键之外的索引(显式或隐式),并且不能使用ALGORITHM=INSTANT执行。此功能与存储引擎无关(支持任何引擎)。有关更多信息,请参见第 10.3.12 节,“不可见索引”。
外键和其他约束
FOREIGN KEY和REFERENCES子句由InnoDB和NDB存储引擎支持,它们实现ADD [CONSTRAINT [*symbol*]] FOREIGN KEY [*index_name*] (...) REFERENCES ... (...)。请参见第 15.1.20.5 节,“外键约束”。对于其他存储引擎,这些子句会被解析但被忽略。
对于ALTER TABLE,与CREATE TABLE不同,如果给定,ADD FOREIGN KEY会忽略*index_name*并使用自动生成的外键名称。作为解决方法,包含CONSTRAINT子句来指定外键名称:
ADD CONSTRAINT *name* FOREIGN KEY (....) ...
重要提示
MySQL 会默默忽略内联的REFERENCES规范,其中引用是作为列规范的一部分定义的。MySQL 只接受作为单独FOREIGN KEY规范的一部分定义的REFERENCES子句。
注意
分区化的InnoDB表不支持外键。这个限制不适用于NDB表,包括那些明确由[LINEAR] KEY分区的表。更多信息,请参见第 26.6.2 节,“与存储引擎相关的分区限制”。
MySQL 服务器和 NDB 集群都支持使用ALTER TABLE来删除外键:
ALTER TABLE *tbl_name* DROP FOREIGN KEY *fk_symbol*;
在同一ALTER TABLE语句中添加和删除外键对于ALTER TABLE ... ALGORITHM=INPLACE是支持的,但对于ALTER TABLE ... ALGORITHM=COPY不支持。
服务器禁止更改可能导致引用完整性丢失的外键列。一个解决方法是在更改列定义之前使用ALTER TABLE ... DROP FOREIGN KEY,然后在之后使用ALTER TABLE ... ADD FOREIGN KEY。禁止的更改示例包括:
-
更改可能不安全的外键列的数据类型。例如,将
VARCHAR(20)更改为VARCHAR(30)是允许的,但将其更改为VARCHAR(1024)是不允许的,因为这会改变存储单个值所需的长度字节数。 -
在非严格模式下将
NULL列更改为NOT NULL是被禁止的,以防止将NULL值转换为默认的非NULL值,而在引用表中没有相应的值。在严格模式下允许此操作,但如果需要任何此类转换,则会返回错误。
ALTER TABLE *tbl_name* RENAME *new_tbl_name* 在内部更改生成的外键约束名称和以字符串“tbl_nameibfk”开头的用户定义的外键约束名称,以反映新表名称。InnoDB将以字符串“tbl_nameibfk”开头的外键约束名称解释为内部生成的名称。
在 MySQL 8.0.16 之前,ALTER TABLE仅允许以下有限版本的CHECK约束添加语法,该语法被解析并忽略:
ADD CHECK (*expr*)
从 MySQL 8.0.16 开始,ALTER TABLE允许为现有表添加、删除或更改CHECK约束:
-
添加新的
CHECK约束:ALTER TABLE *tbl_name* ADD [CONSTRAINT [*symbol*]] CHECK (*expr*) [[NOT] ENFORCED];约束语法元素的含义与
CREATE TABLE相同。请参阅第 15.1.20.6 节,“CHECK 约束”。 -
删除现有命名为*
symbol*的CHECK约束:ALTER TABLE *tbl_name* DROP CHECK *symbol*; -
更改现有
CHECK约束命名为*symbol*是否强制执行:ALTER TABLE *tbl_name* ALTER CHECK *symbol* [NOT] ENFORCED;
DROP CHECK和ALTER CHECK子句是 MySQL 对标准 SQL 的扩展。
从 MySQL 8.0.19 开始,ALTER TABLE允许更通用(符合 SQL 标准)的语法来删除和更改任何类型的现有约束,其中约束类型是根据约束名称确定的:
-
删除现有命名为*
symbol*的约束:ALTER TABLE *tbl_name* DROP CONSTRAINT *symbol*;如果启用了
sql_require_primary_key系统变量,则尝试删除主键会产生错误。 -
更改现有命名为*
symbol*的约束是否强制执行:ALTER TABLE *tbl_name* ALTER CONSTRAINT *symbol* [NOT] ENFORCED;只有
CHECK约束可以更改为不强制执行。所有其他约束类型始终强制执行。
SQL 标准规定所有类型的约束(主键、唯一索引、外键、检查)属于同一命名空间。在 MySQL 中,每种约束类型在每个模式中都有自己的命名空间。因此,每种约束类型的名称在每个模式中必须是唯一的,但不同类型的约束可以具有相同的名称。当多个约束具有相同名称时,DROP CONSTRAINT和ADD CONSTRAINT是模棱两可的,会导致错误。在这种情况下,必须使用特定于约束的语法来修改约束。例如,使用DROP PRIMARY KEY或DROP FOREIGN KEY来删除主键或外键。
如果表更改导致违反强制执行的CHECK约束,则会发生错误,表不会被修改。导致错误的操作示例:
-
尝试向用于
CHECK约束的列添加AUTO_INCREMENT属性。 -
尝试添加强制执行的
CHECK约束或强制执行现有行违反约束条件的非强制执行的CHECK约束。 -
尝试修改、重命名或删除用于
CHECK约束的列,除非在同一语句中也删除该约束。例外:如果CHECK约束仅引用单个列,则删除该列会自动删除约束。
ALTER TABLE *tbl_name* RENAME *new_tbl_name* 会更改内部生成和用户定义的以字符串“tbl_namechk”开头的CHECK约束名称,以反映新表名。MySQL 将以字符串“tbl_namechk”开头的CHECK约束名称解释为内部生成的名称。
更改字符集
要更改表的默认字符集和所有字符列(CHAR, VARCHAR, TEXT)为新字符集,使用如下语句:
ALTER TABLE *tbl_name* CONVERT TO CHARACTER SET *charset_name*;
该语句还会更改所有字符列的排序规则。如果未指定COLLATE子句以指示使用哪种排序规则,则该语句将使用字符集的默认排序规则。如果此排序规则不适用于预期的表使用(例如,如果从区分大小写的排序规则更改为不区分大小写的排序规则),请明确指定排序规则。
对于数据类型为VARCHAR或TEXT之一的列,CONVERT TO CHARACTER SET会根据需要更改数据类型,以确保新列足够长,可以存储与原始列相同数量的字符。例如,TEXT列有两个长度字节,用于存储列中值的字节长度,最多为 65,535。对于latin1的TEXT列,每个字符需要一个字节,因此该列最多可以存储 65,535 个字符。如果将该列转换为utf8mb4,每个字符可能需要多达 4 个字节,因此最大可能长度为 4 × 65,535 = 262,140 字节。该长度不适合TEXT列的长度字节,因此 MySQL 将数据类型转换为MEDIUMTEXT,这是长度字节可以记录值为 262,140 的最小字符串类型。类似地,VARCHAR列可能会转换为MEDIUMTEXT。
为避免发生刚才描述的数据类型更改,不要使用CONVERT TO CHARACTER SET。而是使用MODIFY来更改单个列。例如:
ALTER TABLE t MODIFY latin1_text_col TEXT CHARACTER SET utf8mb4;
ALTER TABLE t MODIFY latin1_varchar_col VARCHAR(*M*) CHARACTER SET utf8mb4;
如果指定 CONVERT TO CHARACTER SET binary,则 CHAR、VARCHAR 和 TEXT 列将转换为它们对应的二进制字符串类型(BINARY、VARBINARY、BLOB)。这意味着这些列不再具有字符集,并且随后的 CONVERT TO 操作不适用于它们。
如果 CONVERT TO CHARACTER SET 操作中的 charset_name 为 DEFAULT,则使用由 character_set_database 系统变量命名的字符集。
警告
CONVERT TO 操作在原始字符集和命名字符集之间转换列值。如果您有一个列使用一个字符集(如 latin1),但存储的值实际上使用另一个不兼容的字符集(如 utf8mb4),那么这不是您想要的。在这种情况下,您必须针对每个这样的列执行以下操作:
ALTER TABLE t1 CHANGE c1 c1 BLOB;
ALTER TABLE t1 CHANGE c1 c1 TEXT CHARACTER SET utf8mb4;
这样做的原因是在转换到或从 BLOB 列时不进行转换。
要仅更改表的 默认 字符集,请使用此语句:
ALTER TABLE *tbl_name* DEFAULT CHARACTER SET *charset_name*;
单词 DEFAULT 是可选的。如果您在以后向表添加列时未指定字符集,则默认字符集是使用的字符集(例如,使用 ALTER TABLE ... ADD column)。
当启用 foreign_key_checks 系统变量时(默认设置),不允许在包含在外键约束中使用字符串列的表上进行字符集转换。解决方法是在执行字符集转换之前禁用 foreign_key_checks。在重新启用 foreign_key_checks 之前,必须对涉及外键约束的两个表执行转换。如果在只转换其中一个表后重新启用 foreign_key_checks,由于在这些操作期间发生的隐式转换,ON DELETE CASCADE 或 ON UPDATE CASCADE 操作可能会损坏引用表中的数据(Bug #45290,Bug #74816)。
导入 InnoDB 表
在其自己的 file-per-table 表空间中创建的 InnoDB 表可以使用 DISCARD TABLESPACE 和 IMPORT TABLESPACE 子句从备份或另一个 MySQL 服务器实例导入。参见 Section 17.6.1.3, “Importing InnoDB Tables”。
MyISAM 表的行顺序
ORDER BY使您能够按特定顺序创建新表中的行。此选项主要在您知道大多数情况下以特定顺序查询行时很有用。通过在对表进行重大更改后使用此选项,您可能能够获得更高的性能。在某些情况下,如果表按照稍后要按其排序的列的顺序排列,可能会使 MySQL 更容易进行排序。
注意
表在插入和删除后不会保持指定的顺序。
ORDER BY语法允许指定一个或多个列名进行排序,每个列名后面可以选择跟随ASC或DESC以指示升序或降序排序顺序。默认为升序。只允许列名作为排序标准;不允许任意表达式。此子句应在任何其他子句之后给出。
对于InnoDB表,ORDER BY没有意义,因为InnoDB始终根据聚簇索引对表行进行排序。
当在分区表上使用ALTER TABLE ... ORDER BY时,仅对每个分区内的行进行排序。
分区选项
*partition_options*表示可用于分区表的选项,用于重新分区、添加、删除、丢弃、导入、合并和拆分分区,以及执行分区维护。
ALTER TABLE语句可以包含PARTITION BY或REMOVE PARTITIONING子句以及其他修改规范,但PARTITION BY或REMOVE PARTITIONING子句必须在任何其他规范之后指定。ADD PARTITION、DROP PARTITION、DISCARD PARTITION、IMPORT PARTITION、COALESCE PARTITION、REORGANIZE PARTITION、EXCHANGE PARTITION、ANALYZE PARTITION、CHECK PARTITION和REPAIR PARTITION选项不能与单个ALTER TABLE中的其他修改规范组合,因为列出的选项仅对单个分区起作用。
有关分区选项的更多信息,请参见第 15.1.20 节,“CREATE TABLE Statement”和第 15.1.9.1 节,“ALTER TABLE Partition Operations”。有关ALTER TABLE ... EXCHANGE PARTITION语句的信息和示例,请参见第 26.3.3 节,“Exchanging Partitions and Subpartitions with Tables”。