MySQL8 中文参考(五十六)
原文:
dev.mysql.com/doc/refman/8.0/en/alter-table-partition-operations.html
15.1.9.1 修改表分区操作
与分区表相关的ALTER TABLE的分区相关子句可用于对分区表进行重新分区,添加、删除、丢弃、导入、合并和拆分分区,以及执行分区维护。
-
在分区表上简单地使用带有
ALTER TABLE的*partition_options子句会根据partition_options定义的分区方案重新分区表。该子句始终以PARTITION BY开头,并遵循与CREATE TABLE的partition_options*子句相同的语法和其他规则(有关更详细信息,请参见第 15.1.20 节,“CREATE TABLE Statement”),也可用于对尚未分区的现有表进行分区。例如,考虑一个(非分区的)表定义如下:CREATE TABLE t1 ( id INT, year_col INT );可以使用以下语句将此表按
HASH进行分区,使用id列作为分区键,分为 8 个分区:ALTER TABLE t1 PARTITION BY HASH(id) PARTITIONS 8;MySQL 支持与
[SUB]PARTITION BY [LINEAR] KEY一起使用的ALGORITHM选项。ALGORITHM=1使服务器在计算行在分区中的放置时使用与 MySQL 5.1 相同的键哈希函数;ALGORITHM=2表示服务器使用 MySQL 5.5 及更高版本中新的KEY分区表默认实现和使用的键哈希函数。 (使用 MySQL 5.5 及更高版本中实施的键哈希函数创建的分区表不能被 MySQL 5.1 服务器使用。)不指定该选项与使用ALGORITHM=2具有相同效果。该选项主要用于在 MySQL 5.1 及更高版本之间升级或降级[LINEAR] KEY分区表,或者在 MySQL 5.5 或更高版本服务器上创建由KEY或LINEAR KEY分区的表,该表可在 MySQL 5.1 服务器上使用。使用
ALTER TABLE ... PARTITION BY语句得到的表必须遵循与使用CREATE TABLE ... PARTITION BY创建的表相同的规则。这包括表可能具有的任何唯一键(包括任何主键)与用于分区表达式的列之间的关系规则,如第 26.6.1 节,“分区键、主键和唯一键”中所讨论的。指定分区数量的CREATE TABLE ... PARTITION BY规则也适用于ALTER TABLE ... PARTITION BY。ALTER TABLE ADD PARTITION的partition_definition子句支持与CREATE TABLE语句中同名子句相同的选项。 (有关语法和描述,请参见 Section 15.1.20, “CREATE TABLE Statement”。)假设您已按此处所示创建了分区表:CREATE TABLE t1 ( id INT, year_col INT ) PARTITION BY RANGE (year_col) ( PARTITION p0 VALUES LESS THAN (1991), PARTITION p1 VALUES LESS THAN (1995), PARTITION p2 VALUES LESS THAN (1999) );您可以按以下方式向此表添加一个新的分区
p3,用于存储小于2002的值:ALTER TABLE t1 ADD PARTITION (PARTITION p3 VALUES LESS THAN (2002));DROP PARTITION可用于删除一个或多个RANGE或LIST分区。此语句不能与HASH或KEY分区一起使用;而应使用COALESCE PARTITION(请参见本节后面的内容)。丢弃在partition_names列表中命名的已删除分区中存储的任何数据。例如,给定先前定义的表t1,您可以按如下所示删除命名为p0和p1的分区:ALTER TABLE t1 DROP PARTITION p0, p1;注意
DROP PARTITION不能用于使用NDB存储引擎的表。请参阅 Section 26.3.1, “RANGE 和 LIST 分区的管理”,以及 Section 25.2.7, “NDB Cluster 的已知限制”。ADD PARTITION和DROP PARTITION目前不支持IF [NOT] EXISTS。DISCARD PARTITION ... TABLESPACE和IMPORT PARTITION ... TABLESPACE选项将可传输表空间功能扩展到单个InnoDB表分区。每个InnoDB表分区都有自己的表空间文件(.ibd文件)。可传输表空间功能使得从一个运行中的 MySQL 服务器实例复制表空间到另一个运行实例,或在同一实例上执行还原变得容易。这两个选项都接受一个逗号分隔的一个或多个分区名称列表。例如:ALTER TABLE t1 DISCARD PARTITION p2, p3 TABLESPACE;ALTER TABLE t1 IMPORT PARTITION p2, p3 TABLESPACE;在对子分区表运行
DISCARD PARTITION ... TABLESPACE和IMPORT PARTITION ... TABLESPACE时,允许使用分区和子分区名称。当指定分区名称时,将包括该分区的子分区。可传输表空间功能还支持复制或还原分区的
InnoDB表。有关更多信息,请参阅 Section 17.6.1.3, “导入 InnoDB 表”。支持对分区表进行重命名。您可以间接地使用
ALTER TABLE ... REORGANIZE PARTITION重命名单个分区;但是,此操作会复制分区的数据。要从选定的分区中删除行,请使用
TRUNCATE PARTITION选项。此选项接受一个或多个逗号分隔的分区名称列表。考虑通过此语句创建的表t1:CREATE TABLE t1 ( id INT, year_col INT ) PARTITION BY RANGE (year_col) ( PARTITION p0 VALUES LESS THAN (1991), PARTITION p1 VALUES LESS THAN (1995), PARTITION p2 VALUES LESS THAN (1999), PARTITION p3 VALUES LESS THAN (2003), PARTITION p4 VALUES LESS THAN (2007) );要从分区
p0中删除所有行,请使用以下语句:ALTER TABLE t1 TRUNCATE PARTITION p0;刚刚显示的语句具有与以下
DELETE语句相同的效果:DELETE FROM t1 WHERE year_col < 1991;在截断多个分区时,分区不必是连续的:这可以极大简化对分区表的删除操作,否则如果使用
DELETE语句进行操作,则需要非常复杂的WHERE条件。例如,此语句删除分区p1和p3中的所有行:ALTER TABLE t1 TRUNCATE PARTITION p1, p3;这里显示了一个等效的
DELETE语句:DELETE FROM t1 WHERE (year_col >= 1991 AND year_col < 1995) OR (year_col >= 2003 AND year_col < 2007);如果在分区名单的位置使用
ALL关键字,则该语句将作用于所有表分区。TRUNCATE PARTITION仅删除行;它不会改变表本身的定义,也不会改变任何分区的定义。要验证行是否已删除,请检查
INFORMATION_SCHEMA.PARTITIONS表,使用类似于以下查询的查询:SELECT PARTITION_NAME, TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't1';COALESCE PARTITION可用于由HASH或KEY分区的表,以减少*number*个分区的数量。假设您已创建表t2如下所示:CREATE TABLE t2 ( name VARCHAR (30), started DATE ) PARTITION BY HASH( YEAR(started) ) PARTITIONS 6;要将
t2使用的分区数量从 6 减少到 4,请使用以下语句:ALTER TABLE t2 COALESCE PARTITION 2;最后*
number*个分区中包含的数据合并到剩余分区中。在这种情况下,分区 4 和 5 合并到前 4 个分区(编号为 0、1、2 和 3 的分区)。要更改分区表中使用的一些但不是所有分区,可以使用
REORGANIZE PARTITION。此语句可以以几种方式使用:-
将一组分区合并为单个分区。通过在*
partition_names列表中命名多个分区并提供partition_definition*的单个定义来完成此操作。 -
将现有分区分割为多个分区。通过为*
partition_names命名单个分区并提供多个partition_definitions*来实现此目的。 -
更改使用
VALUES LESS THAN定义的一部分分区的范围,或者更改使用VALUES IN定义的一部分分区的值列表。
注意
对于未明确命名的分区,MySQL 会自动提供默认名称
p0、p1、p2等。关于子分区也是如此。有关
ALTER TABLE ... REORGANIZE PARTITION语句的更详细信息和示例,请参见第 26.3.1 节,“RANGE 和 LIST 分区的管理”。 -
-
要与表交换分区或子分区,请使用
ALTER TABLE ... EXCHANGE PARTITION语句——即将分区或子分区中的任何现有行移动到非分区表中,并将非分区表中的任何现有行移动到表分区或子分区中。一旦使用
ALGORITHM=INSTANT向分区表添加了一个或多个列,就不再可能与该表交换分区。查看用法信息和示例,请参见第 26.3.3 节,“使用表交换分区和子分区”。
-
几个选项提供了类似于非分区表通过
CHECK TABLE和REPAIR TABLE实现的分区维护和修复功能(这些语句也支持分区表;有关更多信息,请参见第 15.7.3 节,“表维护语句”)。这些包括ANALYZE PARTITION、CHECK PARTITION、OPTIMIZE PARTITION、REBUILD PARTITION和REPAIR PARTITION。每个选项都需要一个*partition_names子句,由一个或多个分区名称组成,用逗号分隔。这些分区必须已经存在于目标表中。您还可以在partition_names*的位置使用ALL关键字,此时该语句将作用于所有表分区。有关更多信息和示例,请参见第 26.3.4 节,“分区维护”。InnoDB目前不支持按分区进行优化;ALTER TABLE ... OPTIMIZE PARTITION会导致整个表被重建和分析,并发出适当的警告。(Bug #11751825,Bug #42822)为解决此问题,请改用ALTER TABLE ... REBUILD PARTITION和ALTER TABLE ... ANALYZE PARTITION。对于未分区的表,不支持
ANALYZE PARTITION、CHECK PARTITION、OPTIMIZE PARTITION和REPAIR PARTITION选项。 -
REMOVE PARTITIONING使您可以删除表的分区,而不影响表或其数据。此选项可以与其他ALTER TABLE选项结合使用,例如用于添加、删除或重命名列或索引的选项。 -
使用
ENGINE选项与ALTER TABLE一起,可以更改表使用的存储引擎,而不影响分区。目标存储引擎必须提供自己的分区处理程序。只有InnoDB和NDB存储引擎具有本机分区处理程序;NDB目前不受 MySQL 8.0 支持。
ALTER TABLE 语句可以包含 PARTITION BY 或 REMOVE PARTITIONING 子句以及其他修改规范,但 PARTITION BY 或 REMOVE PARTITIONING 子句必须在任何其他规范之后指定。
ADD PARTITION、DROP PARTITION、COALESCE PARTITION、REORGANIZE PARTITION、ANALYZE PARTITION、CHECK PARTITION 和 REPAIR PARTITION 选项不能与其他修改规范组合在单个 ALTER TABLE 中,因为上述列出的选项作用于单个分区。有关更多信息,请参见 Section 15.1.9.1, “ALTER TABLE Partition Operations”。
在给定的 ALTER TABLE 语句中,只能使用以下选项中的任何一个的单个实例:PARTITION BY、ADD PARTITION、DROP PARTITION、TRUNCATE PARTITION、EXCHANGE PARTITION、REORGANIZE PARTITION、COALESCE PARTITION、ANALYZE PARTITION、CHECK PARTITION、OPTIMIZE PARTITION、REBUILD PARTITION 或 REMOVE PARTITIONING。
例如,以下两个语句是无效的:
ALTER TABLE t1 ANALYZE PARTITION p1, ANALYZE PARTITION p2;
ALTER TABLE t1 ANALYZE PARTITION p1, CHECK PARTITION p2;
在第一种情况下,你可以使用单个语句并列出要分析的分区 p1 和 p2 的表 t1,同时并行分析这两个分区,如下所示:
ALTER TABLE t1 ANALYZE PARTITION p1, p2;
在第二种情况下,不可能同时对同一表的不同分区执行 ANALYZE 和 CHECK 操作。相反,你必须发出两个单独的语句,如下所示:
ALTER TABLE t1 ANALYZE PARTITION p1;
ALTER TABLE t1 CHECK PARTITION p2;
REBUILD 操作目前不支持子分区。REBUILD 关键字明确禁止与子分区一起使用,并且如果这样使用会导致 ALTER TABLE 失败并显示错误。
当要检查或修复的分区包含任何重复键错误时,CHECK PARTITION 和 REPAIR PARTITION 操作将失败。
有关这些语句的更多信息,请参见 Section 26.3.4, “Maintenance of Partitions”。
原文:
dev.mysql.com/doc/refman/8.0/en/alter-table-generated-columns.html
15.1.9.2 ALTER TABLE 和 Generated Columns
允许对生成列执行的ALTER TABLE操作为ADD、MODIFY和CHANGE。
-
可以添加生成列。
CREATE TABLE t1 (c1 INT); ALTER TABLE t1 ADD COLUMN c2 INT GENERATED ALWAYS AS (c1 + 1) STORED; -
可以修改生成列的数据类型和表达式。
CREATE TABLE t1 (c1 INT, c2 INT GENERATED ALWAYS AS (c1 + 1) STORED); ALTER TABLE t1 MODIFY COLUMN c2 TINYINT GENERATED ALWAYS AS (c1 + 5) STORED; -
如果没有其他列引用生成列,则可以重命名或删除生成列。
CREATE TABLE t1 (c1 INT, c2 INT GENERATED ALWAYS AS (c1 + 1) STORED); ALTER TABLE t1 CHANGE c2 c3 INT GENERATED ALWAYS AS (c1 + 1) STORED; ALTER TABLE t1 DROP COLUMN c3; -
无法将虚拟生成列更改为存储生成列,反之亦然。要解决此问题,请删除列,然后使用新定义添加列。
CREATE TABLE t1 (c1 INT, c2 INT GENERATED ALWAYS AS (c1 + 1) VIRTUAL); ALTER TABLE t1 DROP COLUMN c2; ALTER TABLE t1 ADD COLUMN c2 INT GENERATED ALWAYS AS (c1 + 1) STORED; -
可以将非生成列更改为存储但不是虚拟生成列。
CREATE TABLE t1 (c1 INT, c2 INT); ALTER TABLE t1 MODIFY COLUMN c2 INT GENERATED ALWAYS AS (c1 + 1) STORED; -
可以将存储但不是虚拟生成列更改为非生成列。存储生成的值成为非生成列的值。
CREATE TABLE t1 (c1 INT, c2 INT GENERATED ALWAYS AS (c1 + 1) STORED); ALTER TABLE t1 MODIFY COLUMN c2 INT; -
ADD COLUMN不是对存储列进行原地操作(不使用临时表完成)的操作,因为表达式必须由服务器评估。对于存储列,索引更改是原地完成的,而表达式更改不是原地完成的。对列注释的更改是原地完成的。 -
对于非分区表,对虚拟列进行
ADD COLUMN和DROP COLUMN是原地操作。但是,无法在与其他ALTER TABLE操作组合时原地执行添加或删除虚拟列。对于分区表,对虚拟列进行
ADD COLUMN和DROP COLUMN不是原地操作。 -
InnoDB支持虚拟生成列的辅助索引。在虚拟生成列上添加或删除辅助索引是原地操作。有关更多信息,请参见 Section 15.1.20.9, “Secondary Indexes and Generated Columns”。 -
当向表中添加或修改
VIRTUAL生成列时,不能确保由生成列表达式计算的数据不超出列的范围。这可能导致返回不一致的数据和意外失败的语句。为了允许对这些列进行验证的控制,ALTER TABLE支持WITHOUT VALIDATION和WITH VALIDATION子句:-
使用
WITHOUT VALIDATION(如果未指定任何子句,则为默认值),将执行原地操作(如果可能),不会检查数据完整性,并且语句完成得更快。但是,如果值超出范围,则稍后从表中读取的值可能会报告警告或错误。 -
使用
WITH VALIDATION,ALTER TABLE会复制表。如果发生超出范围或任何其他错误,则语句失败。由于执行了表复制,因此语句需要更长时间。
WITHOUT VALIDATION和WITH VALIDATION只允许与ADD COLUMN、CHANGE COLUMN和MODIFY COLUMN操作一起使用。否则,会出现ER_WRONG_USAGE错误。 -
-
如果表达式评估导致截断或向函数提供不正确的输入,则
ALTER TABLE语句将以错误终止,DDL 操作将被拒绝。 -
一个
ALTER TABLE语句,改变列*col_name的默认值可能会改变引用该列的生成列表达式的值,该生成列表达式使用col_name*,这可能会改变引用该列的生成列表达式的值,该生成列表达式使用DEFAULT(*col_name*)。因此,如果任何生成列表达式使用DEFAULT(),那么改变列定义的ALTER TABLE操作会导致表重建。
原文:
dev.mysql.com/doc/refman/8.0/en/alter-table-examples.html
15.1.9.3 修改表示例
从这里创建一个名为t1的表:
CREATE TABLE t1 (a INTEGER, b CHAR(10));
将表从t1重命名为t2:
ALTER TABLE t1 RENAME t2;
将列a从INTEGER更改为TINYINT NOT NULL(保持名称不变),将列b从CHAR(10)更改为CHAR(20),并将其重命名为c:
ALTER TABLE t2 MODIFY a TINYINT NOT NULL, CHANGE b c CHAR(20);
添加一个名为d的新的TIMESTAMP列:
ALTER TABLE t2 ADD d TIMESTAMP;
在列d上添加一个索引和在列a上添加一个UNIQUE索引:
ALTER TABLE t2 ADD INDEX (d), ADD UNIQUE (a);
要删除列c:
ALTER TABLE t2 DROP COLUMN c;
添加一个名为c的新的AUTO_INCREMENT整数列:
ALTER TABLE t2 ADD c INT UNSIGNED NOT NULL AUTO_INCREMENT,
ADD PRIMARY KEY (c);
我们对c进行了索引(作为PRIMARY KEY),因为AUTO_INCREMENT列必须被索引,并且我们声明c为NOT NULL,因为主键列不能为NULL。
对于NDB表,也可以更改表或列使用的存储类型。例如,考虑一个如下所示创建的NDB表:
mysql> CREATE TABLE t1 (c1 INT) TABLESPACE ts_1 ENGINE NDB;
Query OK, 0 rows affected (1.27 sec)
要将此表转换为基于磁盘的存储,您可以使用以下ALTER TABLE语句:
mysql> ALTER TABLE t1 TABLESPACE ts_1 STORAGE DISK;
Query OK, 0 rows affected (2.99 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW CREATE TABLE t1\G
*************************** 1\. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int(11) DEFAULT NULL
) /*!50100 TABLESPACE ts_1 STORAGE DISK */
ENGINE=ndbcluster DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.01 sec)
在最初创建表时不需要引用表空间;但是,在ALTER TABLE中必须引用表空间:
mysql> CREATE TABLE t2 (c1 INT) ts_1 ENGINE NDB;
Query OK, 0 rows affected (1.00 sec)
mysql> ALTER TABLE t2 STORAGE DISK;
ERROR 1005 (HY000): Can't create table 'c.#sql-1750_3' (errno: 140)
mysql> ALTER TABLE t2 TABLESPACE ts_1 STORAGE DISK;
Query OK, 0 rows affected (3.42 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW CREATE TABLE t2\G
*************************** 1\. row ***************************
Table: t1
Create Table: CREATE TABLE `t2` (
`c1` int(11) DEFAULT NULL
) /*!50100 TABLESPACE ts_1 STORAGE DISK */
ENGINE=ndbcluster DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.01 sec)
要更改单个列的存储类型,可以使用ALTER TABLE ... MODIFY [COLUMN]。例如,假设您使用以下CREATE TABLE语句创建了一个具有两列的 NDB Cluster Disk Data 表:
mysql> CREATE TABLE t3 (c1 INT, c2 INT)
-> TABLESPACE ts_1 STORAGE DISK ENGINE NDB;
Query OK, 0 rows affected (1.34 sec)
要将列c2从基于磁盘的存储更改为内存存储,在 ALTER TABLE 语句中使用列定义中的 STORAGE MEMORY 子句,如下所示:
mysql> ALTER TABLE t3 MODIFY c2 INT STORAGE MEMORY;
Query OK, 0 rows affected (3.14 sec)
Records: 0 Duplicates: 0 Warnings: 0
您可以通过类似的方式使用STORAGE DISK将内存列转换为基于磁盘的列。
列c1使用基于磁盘的存储,因为这是表的默认设置(由CREATE TABLE语句中的表级STORAGE DISK子句确定)。然而,列c2使用内存存储,如在 SHOW CREATE TABLE的输出中所示:
mysql> SHOW CREATE TABLE t3\G
*************************** 1\. row ***************************
Table: t3
Create Table: CREATE TABLE `t3` (
`c1` int(11) DEFAULT NULL,
`c2` int(11) /*!50120 STORAGE MEMORY */ DEFAULT NULL
) /*!50100 TABLESPACE ts_1 STORAGE DISK */ ENGINE=ndbcluster DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.02 sec)
当您添加一个AUTO_INCREMENT列时,列值会自动填充为序列号。对于MyISAM表,您可以在ALTER TABLE之前执行SET INSERT_ID=*value*或使用AUTO_INCREMENT=*value*表选项来设置第一个序列号。
对于MyISAM表,如果不更改AUTO_INCREMENT列,则序列号不受影响。如果删除一个AUTO_INCREMENT列,然后添加另一个AUTO_INCREMENT列,数字将从 1 开始重新排序。
当使用复制时,在表中添加一个AUTO_INCREMENT列可能导致副本和源之间的行排序不同。这是因为行编号的顺序取决于用于表的特定存储引擎以及插入行的顺序。如果在源和副本上具有相同的顺序很重要,则必须在分配AUTO_INCREMENT编号之前对行进行排序。假设您想要向表t1添加一个AUTO_INCREMENT列,以下语句将生成一个新表t2,与t1相同但带有一个AUTO_INCREMENT列:
CREATE TABLE t2 (id INT AUTO_INCREMENT PRIMARY KEY)
SELECT * FROM t1 ORDER BY col1, col2;
这假设表t1具有列col1和col2。
这组语句还会生成一个新表t2,与t1相同,但添加了一个AUTO_INCREMENT列:
CREATE TABLE t2 LIKE t1;
ALTER TABLE t2 ADD id INT AUTO_INCREMENT PRIMARY KEY;
INSERT INTO t2 SELECT * FROM t1 ORDER BY col1, col2;
重要
要保证源和副本上的相同排序,必须在ORDER BY子句中引用t1的所有列。
无论使用何种方法创建和填充具有AUTO_INCREMENT列的副本,最后一步都是删除原始表,然后重命名副本:
DROP TABLE t1;
ALTER TABLE t2 RENAME t1;
15.1.10 修改表空间语句
ALTER [UNDO] TABLESPACE *tablespace_name*
*NDB only:*
{ADD | DROP} DATAFILE '*file_name*'
[INITIAL_SIZE [=] size]
[WAIT]
*InnoDB and NDB:*
[RENAME TO *tablespace_name*]
*InnoDB only:*
[AUTOEXTEND_SIZE [=] '*value*']
[SET {ACTIVE | INACTIVE}]
[ENCRYPTION [=] {'Y' | 'N'}]
*InnoDB and NDB:*
[ENGINE [=] *engine_name*]
*Reserved for future use:*
[ENGINE_ATTRIBUTE [=] '*string*']
此语句用于 NDB 和 InnoDB 表空间。它可用于向 NDB 表空间添加新数据文件,或从中删除数据文件。还可用于重命名 NDB 集群磁盘数据表空间、重命名 InnoDB 通用表空间、加密 InnoDB 通用表空间,或将 InnoDB 撤销表空间标记为活动或非活动。
UNDO 关键字,引入于 MySQL 8.0.14 版本,与 SET {ACTIVE | INACTIVE} 子句一起用于将 InnoDB 撤销表空间标记为活动或非活动。有关更多信息,请参阅 第 17.6.3.4 节,“撤销表空间”。
ADD DATAFILE 变体允许您使用 INITIAL_SIZE 子句指定 NDB 磁盘数据表空间的初始大小,其中 size 以字节为单位;默认值为 134217728(128 MB)。您可以选择性地在 size 后面跟上一个表示数量级的单个字母缩写,类似于 my.cnf 中使用的那些字母。通常,这些字母之一是 M(兆字节)或 G(千兆字节)。
在 32 位系统上,INITIAL_SIZE 的最大支持值为 4294967296(4 GB)。 (Bug #29186)
INITIAL_SIZE 被明确地四舍五入,就像 CREATE TABLESPACE 中一样。
一旦创建了数据文件,其大小就无法更改;但是,您可以使用额外的 ALTER TABLESPACE ... ADD DATAFILE 语句向 NDB 表空间添加更多数据文件。
当 ALTER TABLESPACE ... ADD DATAFILE 与 ENGINE = NDB 一起使用时,在每个集群数据节点上创建一个数据文件,但在信息模式 FILES 表中只生成一行。有关更多信息,请参阅此表的描述,以及 第 25.6.11.1 节,“NDB 集群磁盘数据对象”。ADD DATAFILE 不支持 InnoDB 表空间。
使用 DROP DATAFILE 与 ALTER TABLESPACE 一起,从 NDB 表空间中删除数据文件 'file_name'。您不能从任何表正在使用的表空间中删除数据文件;换句话说,数据文件必须为空(未使用任何范围)。请参阅 第 25.6.11.1 节,“NDB 集群磁盘数据对象”。此外,要删除的任何数据文件必须先使用 CREATE TABLESPACE 或 ALTER TABLESPACE 添加到表空间中。DROP DATAFILE 不支持 InnoDB 表空间。
WAIT会被解析但会被忽略。这是为了未来的扩展。
ENGINE子句,用于指定表空间使用的存储引擎,已被弃用;预计在未来的版本中将被移除。表空间存储引擎由数据字典管理,使ENGINE子句过时。如果指定了存储引擎,它必须与数据字典中定义的表空间存储引擎匹配。与NDB表空间兼容的唯一值为NDB和NDBCLUSTER。
RENAME TO操作会隐式在autocommit模式下执行,不受autocommit设置的影响。
当LOCK TABLES或FLUSH TABLES WITH READ LOCK对位于表空间中的表生效时,无法执行RENAME TO操作。
在重命名表空间时,位于通用表空间中的表会获取独占的元数据锁,这会阻止并发的 DDL。并发的 DML 是支持的。
重命名InnoDB通用表空间需要CREATE TABLESPACE权限。
AUTOEXTEND_SIZE选项定义了当表空间满时InnoDB扩展表空间的量。在 MySQL 8.0.23 中引入。设置必须是 4MB 的倍数。默认设置为 0,这会导致表空间根据隐式默认行为进行扩展。更多信息,请参见 Section 17.6.3.9, “Tablespace AUTOEXTEND_SIZE Configuration”。
ENCRYPTION子句用于为InnoDB通用表空间或mysql系统表空间启用或禁用页面级数据加密。通用表空间的加密支持在 MySQL 8.0.13 中引入。mysql系统表空间的加密支持在 MySQL 8.0.16 中引入。
在启用加密之前,必须安装和配置一个密钥环插件。
从 MySQL 8.0.16 开始,如果启用了table_encryption_privilege_check变量,则需要TABLE_ENCRYPTION_ADMIN权限才能更改具有与default_table_encryption设置不同的ENCRYPTION子句设置的通用表空间。
如果表空间中的任何表属于使用DEFAULT ENCRYPTION='N'定义的模式,则无法为通用表空间启用加密。同样,如果通用表空间中的任何表属于使用DEFAULT ENCRYPTION='Y'定义的模式,则无法禁用加密。DEFAULT ENCRYPTION模式选项是在 MySQL 8.0.16 中引入的。
如果在通用表空间上执行的ALTER TABLESPACE语句不包括ENCRYPTION子句,则表空间将保留其当前的加密状态,不受default_table_encryption设置的影响。
当通用表空间或mysql系统表空间被加密时,存储在表空间中的所有表都会被加密。同样,创建在加密表空间中的表也会被加密。
当更改通用表空间或mysql系统表空间的ENCRYPTION属性时,将使用INPLACE算法。INPLACE算法允许在表空间中存在的表上进行并发 DML。并发 DDL 将被阻止。
有关更多信息,请参见第 17.13 节,“InnoDB 数据静态加密”。
ENGINE_ATTRIBUTE选项(自 MySQL 8.0.21 起可用)用于指定主要存储引擎的表空间属性。该选项保留供将来使用。
允许的值是包含有效JSON文档的字符串文字或空字符串('')。无效的JSON将被拒绝。
ALTER TABLESPACE ts1 ENGINE_ATTRIBUTE='{"*key*":"*value*"}';
ENGINE_ATTRIBUTE的值可以重复而不会出错。在这种情况下,将使用指定的最后一个值。
服务器不会检查ENGINE_ATTRIBUTE的值,也不会在更改表的存储引擎时清除这些值。
不允许更改 JSON 属性值的单个元素。您只能添加或替换属性。
15.1.11 ALTER VIEW Statement
ALTER
[ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}]
[DEFINER = *user*]
[SQL SECURITY { DEFINER | INVOKER }]
VIEW *view_name* [(*column_list*)]
AS *select_statement*
[WITH [CASCADED | LOCAL] CHECK OPTION]
这个语句改变了一个必须存在的视图的定义。语法与CREATE VIEW(请参见第 15.1.23 节,“CREATE VIEW Statement”)类似。这个语句需要视图的CREATE VIEW和DROP权限,并且对SELECT语句中引用的每一列都需要一些权限。只有定义者或具有SET_USER_ID权限(或已弃用的SUPER权限)的用户才被允许执行ALTER VIEW。
15.1.12 CREATE DATABASE Statement
CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] *db_name*
[*create_option*] ...
*create_option*: [DEFAULT] {
CHARACTER SET [=] *charset_name*
| COLLATE [=] *collation_name*
| ENCRYPTION [=] {'Y' | 'N'}
}
CREATE DATABASE使用给定的名称创建数据库。要使用此语句,您需要数据库的CREATE权限。CREATE SCHEMA是CREATE DATABASE的同义词。
如果数据库存在且您未指定IF NOT EXISTS,则会发生错误。
在具有活动LOCK TABLES语句的会话中,不允许使用CREATE DATABASE。
每个*create_option*指定一个数据库特性。数据库特性存储在数据字典中。
-
CHARACTER SET选项指定默认的数据库字符集。COLLATE选项指定默认的数据库校对规则。有关字符集和校对规则名称的信息,请参见 Chapter 12, Character Sets, Collations, Unicode。要查看可用的字符集和校对规则,请分别使用
SHOW CHARACTER SET和SHOW COLLATION语句。参见 Section 15.7.7.3, “SHOW CHARACTER SET Statement”和 Section 15.7.7.4, “SHOW COLLATION Statement”。 -
ENCRYPTION选项是在 MySQL 8.0.16 中引入的,定义了默认的数据库加密,该加密会被创建在数据库中的表继承。允许的值为'Y'(启用加密)和'N'(禁用加密)。如果未指定ENCRYPTION选项,则default_table_encryption系统变量的值定义了默认的数据库加密。如果启用了table_encryption_privilege_check系统变量,则需要TABLE_ENCRYPTION_ADMIN权限来指定与default_table_encryption设置不同的默认加密设置。有关更多信息,请参见为模式和通用表空间定义加密默认值。
在 MySQL 中,数据库被实现为一个包含与数据库中表对应的文件的目录。因为在初始创建数据库时数据库中没有表,所以CREATE DATABASE语句只会在 MySQL 数据目录下创建一个目录。有关可接受数据库名称的规则,请参阅第 11.2 节,“模式对象名称”。如果数据库名称包含特殊字符,则数据库目录的名称将包含这些字符的编码版本,如第 11.2.4 节,“标识符映射到文件名”中所述。
在 MySQL 8.0 中,通过手动在数据目录下创建目录(例如,使用mkdir)来创建数据库目录是不受支持的。
当你创建一个数据库时,让服务器管理目录和其中的文件。直接操作数据库目录和文件可能会导致不一致和意外结果。
MySQL 对数据库数量没有限制。底层文件系统可能对目录数量有限制。
您也可以使用mysqladmin程序来创建数据库。请参阅第 6.5.2 节,“mysqladmin — MySQL 服务器管理程序”。
15.1.13 CREATE EVENT Statement
CREATE
[DEFINER = *user*]
EVENT
[IF NOT EXISTS]
*event_name*
ON SCHEDULE *schedule*
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT '*string*']
DO *event_body*;
*schedule*: {
AT *timestamp* [+ INTERVAL *interval*] ...
| EVERY *interval*
[STARTS *timestamp* [+ INTERVAL *interval*] ...]
[ENDS *timestamp* [+ INTERVAL *interval*] ...]
}
*interval*:
*quantity* {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |
WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |
DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}
此语句创建并安排了一个新事件。除非启用了事件调度程序,否则事件不会运行。有关检查事件调度程序状态并在必要时启用它的信息,请参阅 Section 27.4.2, “Event Scheduler Configuration”。
CREATE EVENT需要在要创建事件的模式中具有EVENT权限。如果存在DEFINER子句,则所需的权限取决于*user*值,如 Section 27.6, “Stored Object Access Control”中所讨论的那样。
CREATE EVENT语句的最低要求如下:
-
包含事件名称的关键字
CREATE EVENT以及一个事件名称,该名称在数据库模式中唯一标识事件。 -
一个
ON SCHEDULE子句,确定事件何时以及多久执行一次。 -
一个包含要由事件执行的 SQL 语句的
DO子句。
这是一个最简单的CREATE EVENT语句的示例:
CREATE EVENT myevent
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
DO
UPDATE myschema.mytable SET mycol = mycol + 1;
上述语句创建了一个名为myevent的事件。此事件在创建后一小时执行一次,通过运行一个 SQL 语句来增加myschema.mytable表的mycol列的值。
*event_name*必须是一个有效的 MySQL 标识符,最大长度为 64 个字符。事件名称不区分大小写,因此在同一模式中不能有两个名为myevent和MyEvent的事件。一般来说,事件名称的规则与存储过程名称的规则相同。请参阅 Section 11.2, “Schema Object Names”。
事件与模式相关联。如果*event_name*的一部分未指定模式,则假定默认(当前)模式。要在特定模式中创建事件,请使用*schema_name*.*event_name*语法限定事件名称。
DEFINER子句指定在事件执行时检查访问权限时要使用的 MySQL 帐户。如果存在DEFINER子句,则*user值应为 MySQL 帐户,指定为'*user_name*'@'*host_name*',CURRENT_USER,或CURRENT_USER()。允许的user*值取决于您拥有的权限,如 Section 27.6, “Stored Object Access Control”中所讨论的那样。还请参阅该部分以获取有关事件安全性的其他信息。
如果省略 DEFINER 子句,则默认的定义者是执行 CREATE EVENT 语句的用户。这与明确指定 DEFINER = CURRENT_USER 是相同的。
在事件体内,CURRENT_USER 函数返回用于在事件执行时检查权限的帐户,即 DEFINER 用户。有关事件内用户审计的信息,请参阅 第 8.2.23 节,“基于 SQL 的帐户活动审计”。
IF NOT EXISTS 对于 CREATE EVENT 与 CREATE TABLE 具有相同的含义:如果同一模式中已经存在名为 event_name 的事件,则不会执行任何操作,也不会产生错误。(但在这种情况下会生成警告。)
ON SCHEDULE 子句确定事件体 event_body 重复定义的事件何时、多久以及持续多久。此子句有两种形式之一:
-
AT *timestamp*用于一次性事件。它指定事件仅在由timestamp给出的日期和时间执行一次,该时间戳必须包括日期和时间,或者必须是解析为日期时间值的表达式。你可以为此目的使用DATETIME或TIMESTAMP类型的值。如果日期已过去,将发出警告,如下所示:mysql> SELECT NOW(); +---------------------+ | NOW() | +---------------------+ | 2006-02-10 23:59:01 | +---------------------+ 1 row in set (0.04 sec) mysql> CREATE EVENT e_totals -> ON SCHEDULE AT '2006-02-10 23:59:00' -> DO INSERT INTO test.totals VALUES (NOW()); Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> SHOW WARNINGS\G *************************** 1\. row *************************** Level: Note Code: 1588 Message: Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation.CREATE EVENT语句本身无效的情况下,无论出于何种原因,都会失败并显示错误。你可以使用
CURRENT_TIMESTAMP来指定当前日期和时间。在这种情况下,事件在创建时立即执行。要创建一个在未来某个时间点发生的事件,相对于当前日期和时间,比如“从现在起三周”,你可以使用可选子句
+ INTERVAL *interval*。interval部分由数量和时间单位组成,并遵循 时间间隔 中描述的语法规则,但在定义事件时不能使用涉及微秒的任何单位关键字。对于某些间隔类型,可以使用复杂的时间单位。例如,“两分钟十秒” 可以表示为+ INTERVAL '2:10' MINUTE_SECOND。你也可以组合时间间隔。例如,
AT CURRENT_TIMESTAMP + INTERVAL 3 WEEK + INTERVAL 2 DAY相当于“从现在起三周零两天”。这样的子句中的每个部分都必须以+ INTERVAL开头。 -
要定期重复执行动作,使用
EVERY子句。EVERY关键字后面跟着一个如前面讨论的AT关键字描述的*间隔*。(EVERY不与+ INTERVAL一起使用。) 例如,EVERY 6 WEEK表示“每六周”。虽然
+ INTERVAL子句在EVERY子句中不允许,但你可以使用与+ INTERVAL允许的相同复杂时间单位。EVERY子句可以包含一个可选的STARTS子句。STARTS后面跟着一个*时间戳*值,表示何时开始重复执行动作,并且还可以使用+ INTERVAL *间隔*来指定“从现在开始”的时间量。例如,EVERY 3 MONTH STARTS CURRENT_TIMESTAMP + INTERVAL 1 WEEK表示“每三个月,从现在开始一周后”。同样,你可以表达“每两周,从现在开始六小时十五分钟后”为EVERY 2 WEEK STARTS CURRENT_TIMESTAMP + INTERVAL '6:15' HOUR_MINUTE。不指定STARTS与使用STARTS CURRENT_TIMESTAMP是一样的——也就是说,事件指定的动作在事件创建时立即开始重复执行。EVERY子句可以包含一个可选的ENDS子句。ENDS关键字后面跟着一个*时间戳*值,告诉 MySQL 事件何时停止重复执行。你也可以在ENDS中使用+ INTERVAL *间隔*;例如,EVERY 12 HOUR STARTS CURRENT_TIMESTAMP + INTERVAL 30 MINUTE ENDS CURRENT_TIMESTAMP + INTERVAL 4 WEEK等同于“每十二小时,从现在开始三十分钟后,结束四周后”。不使用ENDS意味着事件会无限期执行。ENDS支持与STARTS相同的复杂时间单位语法。在
EVERY子句中可以使用STARTS、ENDS、两者或者都不使用。如果重复事件在其调度间隔内没有终止,结果可能是同时执行多个事件实例。如果这是不希望的,你应该建立一个机制来防止同时执行实例。例如,你可以使用
GET_LOCK()函数,或者行或表锁定。
ON SCHEDULE 子句可以使用涉及内置 MySQL 函数和用户变量的表达式来获取其中包含的任何*时间戳或间隔*值。在这样的表达式中,不能使用存储函数或可加载函数,也不能使用任何表引用;但是,你可以使用 SELECT FROM DUAL。这对于 CREATE EVENT 和 ALTER EVENT 语句都适用。在这种情况下,对存储函数、可加载函数和表的引用是明确不允许的,并会报错(参见 Bug #22830)。
ON SCHEDULE子句中的时间使用当前会话的time_zone值进行解释。这成为事件的时区;也就是说,用于事件调度并在事件执行时生效的时区。这些时间被转换为 UTC 并与事件时区一起存储在内部。这使得事件执行可以按照定义进行,而不受服务器时区或夏令时效果的任何后续更改的影响。有关事件时间表示的其他信息,请参见第 27.4.4 节,“事件元数据”。另请参见第 15.7.7.18 节,“SHOW EVENTS 语句”,以及第 28.3.14 节,“INFORMATION_SCHEMA EVENTS 表”。
通常,一旦事件过期,它将立即被删除。您可以通过指定ON COMPLETION PRESERVE来覆盖此行为。使用ON COMPLETION NOT PRESERVE仅仅是明确默认的非持久性行为。
您可以创建一个事件,但使用DISABLE关键字阻止其处于活动状态。或者,您可以使用ENABLE来明确默认状态,即活动状态。这在与ALTER EVENT(参见第 15.1.3 节,“ALTER EVENT 语句”)结合使用时最有用。
除了ENABLE或DISABLE之外,还可以出现第三个值;在副本上设置DISABLE ON SLAVE用于指示事件在副本上的状态,表示事件在复制源服务器上创建并复制到副本,但在副本上不执行。参见第 19.5.1.16 节,“调用功能的复制”。
使用COMMENT子句为事件提供评论。*comment*可以是最多 64 个字符的任何字符串,用于描述事件。评论文本作为字符串文字必须用引号括起来。
DO子句指定事件执行的操作,由 SQL 语句组成。几乎任何可以在存储过程中使用的有效 MySQL 语句也可以作为计划事件的操作语句。(参见第 27.8 节,“存储程序的限制”。)例如,以下事件e_hourly每小时一次从site_activity模式中的sessions表中删除所有行:
CREATE EVENT e_hourly
ON SCHEDULE
EVERY 1 HOUR
COMMENT 'Clears out sessions table each hour.'
DO
DELETE FROM site_activity.sessions;
MySQL 在创建或更改事件时存储了sql_mode系统变量的设置,并始终以此设置执行事件,无论事件开始执行时当前服务器 SQL 模式如何。
包含在其DO子句中的ALTER EVENT语句的CREATE EVENT语句似乎成功;然而,当服务器尝试执行生成的计划事件时,执行将失败并出现错误。
注意
诸如SELECT或SHOW等仅返回结果集的语句在事件中使用时没有任何效果;这些语句的输出既不会发送到 MySQL 监视器,也不会存储在任何地方。但是,您可以使用诸如SELECT ... INTO和INSERT INTO ... SELECT等存储结果的语句。(请参见本节中后续示例中的一个实例。)
事件所属的模式是DO子句中表引用的默认模式。对其他模式中表的引用必须使用正确的模式名称进行限定。
与存储过程一样,您可以在DO子句中使用复合语句语法,通过使用BEGIN和END关键字,如下所示:
delimiter |
CREATE EVENT e_daily
ON SCHEDULE
EVERY 1 DAY
COMMENT 'Saves total number of sessions then clears the table each day'
DO
BEGIN
INSERT INTO site_activity.totals (time, total)
SELECT CURRENT_TIMESTAMP, COUNT(*)
FROM site_activity.sessions;
DELETE FROM site_activity.sessions;
END |
delimiter ;
本示例使用delimiter命令更改语句定界符。请参见第 27.1 节,“定义存储程序”。
在事件中可以使用更复杂的复合语句,例如存储过程中使用的语句。此示例使用了局部变量、错误处理程序和流程控制结构:
delimiter |
CREATE EVENT e
ON SCHEDULE
EVERY 5 SECOND
DO
BEGIN
DECLARE v INTEGER;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;
SET v = 0;
WHILE v < 5 DO
INSERT INTO t1 VALUES (0);
UPDATE t2 SET s1 = s1 + 1;
SET v = v + 1;
END WHILE;
END |
delimiter ;
无法直接传递参数到或从事件中;然而,在事件中调用带参数的存储过程是可能的:
CREATE EVENT e_call_myproc
ON SCHEDULE
AT CURRENT_TIMESTAMP + INTERVAL 1 DAY
DO CALL myproc(5, 27);
如果事件的定义者具有足够设置全局系统变量的权限(参见第 7.1.9.1 节,“系统变量权限”),则事件可以读取和写入全局变量。由于授予此类权限可能导致滥用,因此在执行此操作时必须非常小心。
通常,存储过程中有效的任何语句都可以用于事件执行的操作语句。有关存储过程中允许的语句的更多信息,请参见第 27.2.1 节,“存储过程语法”。不可能将事件创建为存储过程的一部分,也不可能通过另一个事件创建事件。
15.1.14 创建函数语句
CREATE FUNCTION语句用于创建存储函数和可加载函数:
-
关于创建存储函数的信息,请参见第 15.1.17 节,“CREATE PROCEDURE 和 CREATE FUNCTION 语句”。
-
关于创建可加载函数的信息,请参见第 15.7.4.1 节,“用于可加载函数的 CREATE FUNCTION 语句”。
15.1.15 创建索引语句
CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX *index_name*
[*index_type*]
ON *tbl_name* (*key_part*,...)
[*index_option*]
[*algorithm_option* | *lock_option*] ...
*key_part*: {*col_name* [(*length*)] | (*expr*)} [ASC | DESC]
*index_option*: {
KEY_BLOCK_SIZE [=] *value*
| *index_type*
| WITH PARSER *parser_name*
| COMMENT '*string*'
| {VISIBLE | INVISIBLE}
| ENGINE_ATTRIBUTE [=] '*string*'
| SECONDARY_ENGINE_ATTRIBUTE [=] '*string*'
}
*index_type*:
USING {BTREE | HASH}
*algorithm_option*:
ALGORITHM [=] {DEFAULT | INPLACE | COPY}
*lock_option*:
LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}
通常,在创建表本身时,您会在表上创建所有索引,使用创建表。参见第 15.1.20 节,“创建表语句”。这个指导原则对于InnoDB表尤为重要,因为主键决定了数据文件中行的物理布局。创建索引使您能够向现有表添加索引。
创建索引被映射到一个修改表语句来创建索引。参见第 15.1.9 节,“修改表语句”。创建索引不能用于创建主键;请使用修改表。有关索引的更多信息,请参见第 10.3.1 节,“MySQL 如何使用索引”。
InnoDB支持虚拟列上的辅助索引。有关更多信息,请参见第 15.1.20.9 节,“辅助索引和生成列”。
当启用innodb_stats_persistent设置时,在表上创建索引后,运行分析表语句来分析InnoDB表。
从 MySQL 8.0.17 开始,*expr的key_part*规范可以采用(CAST *json_expression* AS *type* ARRAY)的形式,在JSON列上创建多值索引。参见多值索引。
一个形如(*key_part1*, *key_part2*, ...)的索引规范创建了一个具有多个关键部分的索引。索引键值是通过连接给定关键部分的值形成的。例如(col1, col2, col3)指定了一个多列索引,其索引键由col1、col2和col3的值组成。
key_part 规范可以以 ASC 或 DESC 结尾,以指定索引值是按升序还是降序存储。如果没有给出顺序说明符,则默认为升序。对于 HASH 索引,不允许使用 ASC 和 DESC。对于多值索引,也不支持 ASC 和 DESC。从 MySQL 8.0.12 开始,不允许对 SPATIAL 索引使用 ASC 和 DESC。
以下各节描述了 CREATE INDEX 语句的不同方面:
-
列前缀键部分
-
功能键部分
-
唯一索引
-
全文索引
-
多值索引
-
空间索引
-
索引选项
-
表复制和锁定选项
列前缀键部分
对于字符串列,可以创建仅使用列值前导部分的索引,使用 *col_name*(*length*) 语法来指定索引前缀长度:
-
对于
CHAR、VARCHAR、BINARY和VARBINARY键部分,可以指定前缀。 -
对于
BLOB和TEXT键部分,必须指定前缀。此外,BLOB和TEXT列只能为InnoDB、MyISAM和BLACKHOLE表创建索引。 -
前缀限制以字节为单位。但是,在
CREATE TABLE、ALTER TABLE和CREATE INDEX语句中的索引规范中,对于非二进制字符串类型(CHAR、VARCHAR、TEXT)的索引长度被解释为字符数,对于二进制字符串类型(BINARY、VARBINARY、BLOB)的索引长度被解释为字节数。在为使用多字节字符集的非二进制字符串列指定前缀长度时,请考虑这一点。前缀支持和前缀长度(在支持的情况下)取决于存储引擎。例如,对于使用
REDUNDANT或COMPACT行格式的InnoDB表,前缀可以长达 767 字节。对于使用DYNAMIC或COMPRESSED行格式的InnoDB表,前缀长度限制为 3072 字节。对于MyISAM表,前缀长度限制为 1000 字节。NDB存储引擎不支持前缀(参见 Section 25.2.7.6, “Unsupported or Missing Features in NDB Cluster”)。
如果指定的索引前缀超过最大列数据类型大小,CREATE INDEX将处理索引如下:
-
对于非唯一索引,如果启用了严格的 SQL 模式,将会出现错误;如果未启用严格的 SQL 模式,则会将索引长度减少到不超过最大列数据类型大小,并产生警告。
-
对于唯一索引,无论 SQL 模式如何,都会出现错误,因为减少索引长度可能会导致插入不符合指定唯一性要求的非唯一条目。
此处显示的语句使用name列的前 10 个字符创建索引(假设name是非二进制字符串类型)。
CREATE INDEX part_of_name ON customer (name(10));
如果列中的名称通常在前 10 个字符不同,使用此索引进行查找的速度不应比使用从整个name列创建的索引慢得多。此外,使用列前缀进行索引可以使索引文件更小,这可以节省大量磁盘空间,也可能加快INSERT操作的速度。
函数键部分
“普通”索引索引列值或列值的前缀。例如,在以下表中,对于给定的t1行,索引条目包括完整的col1值和由其前 10 个字符组成的col2值的前缀:
CREATE TABLE t1 (
col1 VARCHAR(10),
col2 VARCHAR(20),
INDEX (col1, col2(10))
);
MySQL 8.0.13 及更高版本支持索引表达式值而不是列或列前缀值的函数键部分。使用函数键部分可以索引表中未直接存储的值。例如:
CREATE TABLE t1 (col1 INT, col2 INT, INDEX func_index ((ABS(col1))));
CREATE INDEX idx1 ON t1 ((col1 + col2));
CREATE INDEX idx2 ON t1 ((col1 + col2), (col1 - col2), col1);
ALTER TABLE t1 ADD INDEX ((col1 * 40) DESC);
具有多个键部分的索引可以混合非函数和函数键部分。
ASC和DESC对于函数键部分是支持的。
函数键部分必须遵循以下规则。如果键部分定义包含不允许的结构,则会发生错误。
-
在索引定义中,将表达式括在括号中以区分它们与列或列前缀。例如,这是被允许的;表达式被括在括号中:
INDEX ((col1 + col2), (col3 - col4))这会产生一个错误;表达式没有被括在括号中:
INDEX (col1 + col2, col3 - col4) -
函数键部分不能仅由列名组成。例如,这是不被允许的:
INDEX ((col1), (col2))相反,将键部分写为非函数键部分,不使用括号:
INDEX (col1, col2) -
函数键部分表达式不能引用列前缀。有关解决方法,请参阅本节后面关于
SUBSTRING()和CAST()的讨论。 -
函数键部分在外键规范中是不被允许的。
对于CREATE TABLE ... LIKE,目标表会保留原始表的函数键部分。
函数索引被实现为隐藏的虚拟生成列,这带来了以下影响:
-
每个函数键部分都计入表列总数的限制;参见第 10.4.7 节,“表列数和行大小限制”。
-
函数键部分继承了适用于生成列的所有限制。例如:
-
只有对于生成列允许的函数才允许用于函数键部分。
-
子查询、参数、变量、存储函数和可加载函数不被允许。
有关适用限制的更多信息,请参见第 15.1.20.8 节,“CREATE TABLE and Generated Columns”,以及第 15.1.9.2 节,“ALTER TABLE and Generated Columns”。
-
-
虚拟生成列本身不需要存储。索引本身占用存储空间,就像任何其他索引一样。
对于包含功能键部分的索引,支持UNIQUE。但是,主键不能包含功能键部分。主键需要存储生成列,但功能键部分实现为虚拟生成列,而不是存储生成列。
SPATIAL和FULLTEXT索引不能具有功能键部分。
如果表不包含主键,则InnoDB会自动将第一个UNIQUE NOT NULL索引提升为主键。对于具有功能键部分的UNIQUE NOT NULL索引,不支持此操作。
如果存在重复索引,非功能索引会引发警告。包含功能键部分的索引不具有此功能。
要删除被功能键部分引用的列,必须首先删除索引。否则,会出现错误。
虽然非功能键部分支持前缀长度规范,但对于功能键部分则不可能。解决方法是使用SUBSTRING()(或CAST(),如本节后面所述)。要在查询中使用包含SUBSTRING()函数的功能键部分,WHERE子句必须包含具有相同参数的SUBSTRING()。在以下示例中,只有第二个SELECT能够使用索引,因为这是唯一一个参数与SUBSTRING()函数匹配索引规范的查询:
CREATE TABLE tbl (
col1 LONGTEXT,
INDEX idx1 ((SUBSTRING(col1, 1, 10)))
);
SELECT * FROM tbl WHERE SUBSTRING(col1, 1, 9) = '123456789';
SELECT * FROM tbl WHERE SUBSTRING(col1, 1, 10) = '1234567890';
功能键部分使得可以对无法以其他方式索引的值进行索引,例如JSON值。但是,必须正确执行才能实现期望的效果。例如,以下语法不起作用:
CREATE TABLE employees (
data JSON,
INDEX ((data->>'$.name'))
);
语法失败的原因是:
-
运算符
->>转换为JSON_UNQUOTE(JSON_EXTRACT(...))。 -
JSON_UNQUOTE()返回一个数据类型为LONGTEXT的值,因此隐藏的生成列被分配相同的数据类型。 -
MySQL 无法对未在键部分指定前缀长度的
LONGTEXT列进行索引,并且功能键部分不允许前缀长度。
要对JSON列进行索引,您可以尝试使用CAST()函数,如下所示:
CREATE TABLE employees (
data JSON,
INDEX ((CAST(data->>'$.name' AS CHAR(30))))
);
隐藏的生成列被分配了VARCHAR(30)数据类型,可以被索引。但是当尝试使用索引时,这种方法会产生一个新问题:
-
CAST()返回一个带有整理utf8mb4_0900_ai_ci(服务器默认整理)的字符串。 -
JSON_UNQUOTE()返回一个带有整理utf8mb4_bin(硬编码)的字符串。
由于在前面的表定义中索引表达式与后续查询中的WHERE子句表达式之间存在整理不匹配,因此索引未被使用:
SELECT * FROM employees WHERE data->>'$.name' = 'James';
由于查询和索引中的表达式不同,索引未被使用。为了支持这种情况下的功能键部分,优化器在寻找要使用的索引时会自动剥离CAST(),但仅当索引表达式的整理与查询表达式的整理匹配时。为了使用具有功能键部分的索引,以下两种解决方案都有效(尽管在效果上略有不同):
-
解决方案 1. 将索引表达式分配与
JSON_UNQUOTE()相同的整理:CREATE TABLE employees ( data JSON, INDEX idx ((CAST(data->>"$.name" AS CHAR(30)) COLLATE utf8mb4_bin)) ); INSERT INTO employees VALUES ('{ "name": "james", "salary": 9000 }'), ('{ "name": "James", "salary": 10000 }'), ('{ "name": "Mary", "salary": 12000 }'), ('{ "name": "Peter", "salary": 8000 }'); SELECT * FROM employees WHERE data->>'$.name' = 'James';->>运算符与JSON_UNQUOTE(JSON_EXTRACT(...))相同,而JSON_UNQUOTE()返回一个带有整理utf8mb4_bin的字符串。因此比较是区分大小写的,只有一行匹配:+------------------------------------+ | data | +------------------------------------+ | {"name": "James", "salary": 10000} | +------------------------------------+ -
解决方案 2. 在查询中指定完整表达式:
CREATE TABLE employees ( data JSON, INDEX idx ((CAST(data->>"$.name" AS CHAR(30)))) ); INSERT INTO employees VALUES ('{ "name": "james", "salary": 9000 }'), ('{ "name": "James", "salary": 10000 }'), ('{ "name": "Mary", "salary": 12000 }'), ('{ "name": "Peter", "salary": 8000 }'); SELECT * FROM employees WHERE CAST(data->>'$.name' AS CHAR(30)) = 'James';CAST()返回一个带有整理utf8mb4_0900_ai_ci的字符串,因此比较不区分大小写,两行匹配:+------------------------------------+ | data | +------------------------------------+ | {"name": "james", "salary": 9000} | | {"name": "James", "salary": 10000} | +------------------------------------+
请注意,尽管优化器支持自动剥离CAST()与索引生成列,但以下方法不起作用,因为它在有索引和无索引时产生不同的结果(Bug#27337092):
mysql> CREATE TABLE employees (
data JSON,
generated_col VARCHAR(30) AS (CAST(data->>'$.name' AS CHAR(30)))
);
Query OK, 0 rows affected, 1 warning (0.03 sec)
mysql> INSERT INTO employees (data)
VALUES ('{"name": "james"}'), ('{"name": "James"}');
Query OK, 2 rows affected, 1 warning (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 1
mysql> SELECT * FROM employees WHERE data->>'$.name' = 'James';
+-------------------+---------------+
| data | generated_col |
+-------------------+---------------+
| {"name": "James"} | James |
+-------------------+---------------+
1 row in set (0.00 sec)
mysql> ALTER TABLE employees ADD INDEX idx (generated_col);
Query OK, 0 rows affected, 1 warning (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 1
mysql> SELECT * FROM employees WHERE data->>'$.name' = 'James';
+-------------------+---------------+
| data | generated_col |
+-------------------+---------------+
| {"name": "james"} | james |
| {"name": "James"} | James |
+-------------------+---------------+
2 rows in set (0.01 sec)
唯一索引
UNIQUE索引创建一个约束,使索引中的所有值必须是不同的。如果尝试添加一个具有与现有行匹配的键值的新行,则会发生错误。如果在UNIQUE索引中为列指定前缀值,则列值必须在前缀长度内是唯一的。UNIQUE索引允许对可以包含NULL的列有多个NULL值。
如果一个表有一个由单个整数类型列组成的PRIMARY KEY或UNIQUE NOT NULL索引,您可以在SELECT语句中使用_rowid来引用索引列,如下所示:
-
_rowid指的是如果有由单个整数列组成的PRIMARY KEY,则指的是PRIMARY KEY列。如果有PRIMARY KEY但它不由单个整数列组成,则无法使用_rowid。 -
否则,
_rowid指的是第一个UNIQUE NOT NULL索引中的列,如果该索引由单个整数列组成。如果第一个UNIQUE NOT NULL索引不包含单个整数列,则无法使用_rowid。
全文索引
仅支持InnoDB和MyISAM表的FULLTEXT索引,只能包括CHAR、VARCHAR和TEXT列。索引始终在整个列上进行;不支持列前缀索引,如果指定了任何前缀长度,则会被忽略。有关操作的详细信息,请参见第 14.9 节,“全文搜索函数”。
多值索引
截至 MySQL 8.0.17,InnoDB支持多值索引。多值索引是定义在存储值数组的列上的辅助索引。一个“正常”的索引对应每个数据记录一个索引记录(1:1)。一个多值索引可以对应单个数据记录多个索引记录(N:1)。多值索引用于对JSON数组进行索引。例如,在以下 JSON 文档中对邮政编码数组定义的多值索引为每个邮政编码创建一个索引记录,每个索引记录引用相同的数据���录。
{
"user":"Bob",
"user_id":31,
"zipcode":[94477,94536]
}
创建多值索引
您可以在CREATE TABLE、ALTER TABLE或CREATE INDEX语句中创建多值索引。这需要在索引定义中使用CAST(... AS ... ARRAY),将JSON数组中的相同类型的标量值转换为 SQL 数据类型数组。然后,一个虚拟列会透明地生成,其中包含 SQL 数据类型数组中的值;最后,在虚拟列上创建一个函数索引(也称为虚拟索引)。这是在来自 SQL 数据类型数组的值的虚拟列上定义的函数索引形成了多值索引。
下面的示例展示了在名为customers的表中的JSON列custinfo上的数组$.zipcode上可以创建多值索引zips的三种不同方式。在每种情况下,JSON 数组被转换为UNSIGNED整数值的 SQL 数据类型数组。
-
仅
CREATE TABLE:CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, custinfo JSON, INDEX zips( (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) ) ); -
CREATE TABLE加上ALTER TABLE:CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, custinfo JSON ); ALTER TABLE customers ADD INDEX zips( (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) ); -
CREATE TABLE加上CREATE INDEX:CREATE TABLE customers ( id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, custinfo JSON ); CREATE INDEX zips ON customers ( (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) );
多值索引也可以作为复合索引的一部分定义。此示例显示了一个包含两个单值部分(用于id和modified列)和一个多值部分(用于custinfo列)的复合索引:
CREATE TABLE customers (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
custinfo JSON
);
ALTER TABLE customers ADD INDEX comp(id, modified,
(CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) );
复合索引中只能使用一个多值键部分。多值键部分可以相对于键的其他部分以任何顺序使用。换句话说,刚刚展示的ALTER TABLE语句可以使用comp(id, (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY), modified))(或任何其他顺序)仍然有效。
使用多值索引
当在WHERE子句中指定以下函数时,优化器使用多值索引来获取记录:
-
MEMBER OF() -
JSON_CONTAINS() -
JSON_OVERLAPS()
我们可以通过使用以下CREATE TABLE和INSERT语句创建和填充customers表来演示这一点:
mysql> CREATE TABLE customers (
-> id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> modified DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-> custinfo JSON
-> );
Query OK, 0 rows affected (0.51 sec)
mysql> INSERT INTO customers VALUES
-> (NULL, NOW(), '{"user":"Jack","user_id":37,"zipcode":[94582,94536]}'),
-> (NULL, NOW(), '{"user":"Jill","user_id":22,"zipcode":[94568,94507,94582]}'),
-> (NULL, NOW(), '{"user":"Bob","user_id":31,"zipcode":[94477,94507]}'),
-> (NULL, NOW(), '{"user":"Mary","user_id":72,"zipcode":[94536]}'),
-> (NULL, NOW(), '{"user":"Ted","user_id":56,"zipcode":[94507,94582]}');
Query OK, 5 rows affected (0.07 sec)
Records: 5 Duplicates: 0 Warnings: 0
首先,我们在customers表上执行三个查询,分别使用MEMBER OF(),JSON_CONTAINS()和JSON_OVERLAPS(),每个查询的结果如下所示:
mysql> SELECT * FROM customers
-> WHERE 94507 MEMBER OF(custinfo->'$.zipcode');
+----+---------------------+-------------------------------------------------------------------+
| id | modified | custinfo |
+----+---------------------+-------------------------------------------------------------------+
| 2 | 2019-06-29 22:23:12 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
| 3 | 2019-06-29 22:23:12 | {"user": "Bob", "user_id": 31, "zipcode": [94477, 94507]} |
| 5 | 2019-06-29 22:23:12 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]} |
+----+---------------------+-------------------------------------------------------------------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM customers
-> WHERE JSON_CONTAINS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+---------------------+-------------------------------------------------------------------+
| id | modified | custinfo |
+----+---------------------+-------------------------------------------------------------------+
| 2 | 2019-06-29 22:23:12 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
| 5 | 2019-06-29 22:23:12 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]} |
+----+---------------------+-------------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> SELECT * FROM customers
-> WHERE JSON_OVERLAPS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+---------------------+-------------------------------------------------------------------+
| id | modified | custinfo |
+----+---------------------+-------------------------------------------------------------------+
| 1 | 2019-06-29 22:23:12 | {"user": "Jack", "user_id": 37, "zipcode": [94582, 94536]} |
| 2 | 2019-06-29 22:23:12 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
| 3 | 2019-06-29 22:23:12 | {"user": "Bob", "user_id": 31, "zipcode": [94477, 94507]} |
| 5 | 2019-06-29 22:23:12 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]} |
+----+---------------------+-------------------------------------------------------------------+
4 rows in set (0.00 sec)
接下来,我们对之前的三个查询中的每个运行EXPLAIN:
mysql> EXPLAIN SELECT * FROM customers
-> WHERE 94507 MEMBER OF(custinfo->'$.zipcode');
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> EXPLAIN SELECT * FROM customers
-> WHERE JSON_CONTAINS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> EXPLAIN SELECT * FROM customers
-> WHERE JSON_OVERLAPS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
刚刚展示的三个查询都无法使用任何键。为了解决这个问题,我们可以在JSON列(custinfo)中的zipcode数组上添加一个多值索引,如下所示:
mysql> ALTER TABLE customers
-> ADD INDEX zips( (CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)) );
Query OK, 0 rows affected (0.47 sec)
Records: 0 Duplicates: 0 Warnings: 0
当我们再次运行之前的EXPLAIN语句时,我们现在可以观察到查询可以(并且确实)使用刚刚创建的索引zips:
mysql> EXPLAIN SELECT * FROM customers
-> WHERE 94507 MEMBER OF(custinfo->'$.zipcode');
+----+-------------+-----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | ref | zips | zips | 9 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> EXPLAIN SELECT * FROM customers
-> WHERE JSON_CONTAINS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | range | zips | zips | 9 | NULL | 6 | 100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> EXPLAIN SELECT * FROM customers
-> WHERE JSON_OVERLAPS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | customers | NULL | range | zips | zips | 9 | NULL | 6 | 100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)
多值索引可以定义为唯一键。如果定义为唯一键,并尝试插入已经存在于多值索引中的值,则会返回重复键错误。如果已经存在重复值,则尝试添加唯一多值索引将失败,如下所示:
mysql> ALTER TABLE customers DROP INDEX zips;
Query OK, 0 rows affected (0.55 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE customers
-> ADD UNIQUE INDEX zips((CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)));
ERROR 1062 (23000): Duplicate entry '94507, ' for key 'customers.zips'
mysql> ALTER TABLE customers
-> ADD INDEX zips((CAST(custinfo->'$.zipcode' AS UNSIGNED ARRAY)));
Query OK, 0 rows affected (0.36 sec)
Records: 0 Duplicates: 0 Warnings: 0
多值索引的特性
多值索引具有以下附加特性:
-
影响多值索引的 DML 操作与影响普通索引的 DML 操作处理方式相同,唯一的区别是对于单个聚集索引记录可能有多个插入或更新。
-
空值和多值索引:
-
如果多值键部分具有空数组,则不会向索引添加任何条目,并且数据记录不可通过索引扫描访问。
-
如果多值键部分生成返回
NULL值,则将添加一个包含NULL的条目到多值索引中。如果键部分被定义为NOT NULL,则会报告错误。 -
如果类型化数组列设置为
NULL,存储引擎将存储一个指向数据记录的包含NULL的单个记录。 -
在索引数组中不允许
JSON空值。如果任何返回值为NULL,则将其视为 JSON 空值,并报告无效的 JSON 值错误。
-
-
因为多值索引是虚拟列上的虚拟索引,所以它们必须遵守与虚拟生成列上的二级索引相同的规则。
-
对于空数组不会添加索引记录。
多值索引的限制和限制。
多值索引受到以下列出的限制和限制的约束:
-
每个多值索引只允许一个多值键部分。但是,[
CAST(... AS ... ARRAY)表达式可以引用JSON文档中的多个数组,如下所示:CAST(data->'$.arr[*][*]' AS UNSIGNED ARRAY)在这种情况下,与
JSON表达式匹配的所有值都作为单个扁平数组存储在索引中。 -
具有多值键部分的索引不支持排序,因此不能用作主键。出于同样的原因,不能使用
ASC或DESC关键字定义多值索引。 -
多值索引不能是覆盖索引。
-
多值索引每个记录的最大值数量由可以存储在单个撤销日志页上的数据量确定,即 65221 字节(64K 减去 315 字节的开销),这意味着键值的最大总长度也是 65221 字节。键的最大数量取决于各种因素,这阻止了定义特定限制。例如,测试表明,多值索引允许每个记录最多有 1604 个整数键。当达到限制时,会报告类似以下的错误:ERROR 3905 (HY000): Exceeded max number of values per record for multi-valued index 'idx' by 1 value(s)。
-
多值键部分中允许的唯一类型表达式是
JSON表达式。表达式不需要引用插入到索引列中的JSON文档中的现有元素,但必须本身在语法上有效。 -
因为相同聚集索引记录的索引记录分散在多值索引中,所以多值索引不支持范围扫描或仅索引扫描。
-
外键规范中不允许多值索引。
-
索引前缀不能为多值索引定义。
-
不能在转换为
BINARY的数据上定义多值索引(请参阅CAST()函数的描述)。 -
不支持在线创建多值索引,这意味着操作使用
ALGORITHM=COPY。请参阅性能和空间要求。 -
不支持以下两种字符集和排序规则以外的字符集和排序规则用于多值索引:
-
使用默认
binary排序规则的binary字符集 -
使用默认
utf8mb4_0900_as_cs排序规则的utf8mb4字符集。
-
-
与
InnoDB表列上的其他索引一样,多值索引不能使用USING HASH创建;尝试这样做会导致警告:This storage engine does not support the HASH index algorithm, storage engine default was used instead.(USING BTREE像往常一样受支持。)
空间索引
MyISAM、InnoDB、NDB和ARCHIVE存储引擎支持诸如POINT和GEOMETRY之类的空间列。(第 13.4 节,“空间数据类型”,描述了空间数据类型。)然而,对于不同存储引擎,对空间列索引的支持有所不同。根据以下规则,空间列上的空间和非空间索引是可用的。
空间列上的空间索引具有以下特点:
-
仅适用于
InnoDB和MyISAM表。为其他存储引擎指定SPATIAL INDEX会导致错误。 -
从 MySQL 8.0.12 开始,空间列上的索引必须是
SPATIAL索引。因此,对于在空间列上创建索引,SPATIAL关键字是可选的,但是隐含的。 -
仅适用于单个空间列。空间索引不能在多个空间列上创建。
-
索引列必须是
NOT NULL。 -
列前缀长度是被禁止的。每列的完整宽度都被索引。
-
不允许用于主键或唯一索引。
空间列上的非空间索引(使用INDEX、UNIQUE或PRIMARY KEY创建)具有以下特点:
-
除了
ARCHIVE之外,任何支持空间列的存储引擎都允许。 -
列可以是
NULL,除非索引是主键。 -
非
SPATIAL索引的索引类型取决于存储引擎。目前使用的是 B-tree。 -
仅适用于只能具有
NULL值的列,对于InnoDB、MyISAM和MEMORY表。
索引选项
在键部分列表之后,可以给出索引选项。*index_option*值可以是以下任何一种:
-
KEY_BLOCK_SIZE [=] *value*对于
MyISAM表,KEY_BLOCK_SIZE可选地指定用于索引键块的字节大小。该值被视为提示;如果需要,可以使用不同的大小。为单个索引定义指定的KEY_BLOCK_SIZE值会覆盖表级别的KEY_BLOCK_SIZE值。对于
InnoDB表,不支持在索引级别上使用KEY_BLOCK_SIZE。请参阅第 15.1.20 节,“CREATE TABLE 语句”。 -
index_type一些存储引擎允许您在创建索引时指定索引类型。例如:
CREATE TABLE lookup (id INT) ENGINE = MEMORY; CREATE INDEX id_index ON lookup (id) USING BTREE;表 15.1,“存储引擎的索引类型”显示了不同存储引擎支持的允许的索引类型值。当没有索引类型说明符时,默认情况下使用第一个索引类型。表中未列出的存储引擎不支持索引定义中的*
index_type*子句。表 15.1 存储引擎的索引类型
存储引擎 允许的索引类型 InnoDBBTREEMyISAMBTREEMEMORY/HEAPHASH,BTREENDBHASH,BTREE(见文本中的注释)*
index_type*子句不能用于FULLTEXT INDEX或(在 MySQL 8.0.12 之前)SPATIAL INDEX规范。全文索引实现取决于存储引擎。空间索引实现为 R 树索引。如果您指定了对于给定存储引擎无效的索引类型,但是引擎可以使用另一种可用类型而不影响查询结果,则引擎将使用可用类型。解析器将
RTREE识别为一种类型名称。从 MySQL 8.0.12 开始,这仅允许用于SPATIAL索引。在 8.0.12 之前,RTREE不能为任何存储引擎指定。BTREE索引由NDB存储引擎实现为 T 树索引。注意
对于
NDB表列上的索引,USING选项只能用于唯一索引或主键。USING HASH阻止有序索引的创建;否则,在NDB表上创建唯一索引或主键将自动导致有序索引和哈希索引的创建,每个索引相同的列集。对于包含一个或多个
NULL列的NDB表的唯一索引,哈希索引只能用于查找文字值,这意味着IS [NOT] NULL条件需要对表进行全面扫描。一个解决方法是确保在这种表上始终以包含有序索引的方式创建使用一个或多个NULL列的唯一索引;也就是说,在创建索引时避免使用USING HASH。如果您指定了对于给定存储引擎无效的索引类型,但另一种索引类型可用且不会影响查询结果,那么引擎将使用可用的类型。解析器将
RTREE识别为一种类型名称,但目前不能为任何存储引擎指定此类型。注意
在
ON *tbl_name*子句之前使用*index_type选项已被弃用;预计在未来的 MySQL 版本中将删除在此位置使用该选项的支持。如果在较早和较晚的位置都给出了index_type*选项,则最终选项生效。TYPE *type_name*被识别为USING *type_name*的同义词。然而,USING是首选形式。下表显示了支持*
index_type*选项的存储引擎的索引特性。表 15.2 InnoDB 存储引擎索引特性
索引类别 索引类型 存储 NULL 值 允许多个 NULL 值 IS NULL 扫描类型 IS NOT NULL 扫描类型 主键 BTREE否 否 N/A N/A 独特 BTREE是 是 索引 索引 键 BTREE是 是 索引 索引 FULLTEXTN/A 是 是 表 表 SPATIALN/A 否 否 N/A N/A 表 15.3 MyISAM 存储引擎索引特性
索引类别 索引类型 存储 NULL 值 允���多个 NULL 值 IS NULL 扫描类型 IS NOT NULL 扫描类型 主键 BTREE否 否 N/A N/A 独特 BTREE是 是 索引 索引 键 BTREE是 是 索引 索引 FULLTEXTN/A 是 是 表 表 SPATIALN/A 否 否 N/A N/A 表 15.4 MEMORY 存储引擎索引特性
索引类别 索引类型 存储 NULL 值 允许多个 NULL 值 IS NULL 扫描类型 IS NOT NULL 扫描类型 主键 BTREE否 否 N/A N/A 独特 BTREE是 是 索引 索引 键 BTREE是 是 索引 索引 主键 HASH否 否 N/A N/A 独特 HASH是 是 索引 索引 键 HASH是 是 索引 索引 表 15.5 NDB 存储引擎索引特性
索引类别 索引类型 存储 NULL 值 允许多个 NULL 值 IS NULL 扫描类型 IS NOT NULL 扫描类型 主键 BTREE否 否 索引 索引 独特 BTREE是 是 索引 索引 键 BTREE是 是 索引 索引 主键 HASH否 否 表(见注 1) 表(见注 1) 唯一 HASH是 是 表(见注 1) 表(见注 1) 键 HASH是 是 表(见注 1) 表(见注 1) 表注释:
1.
USING HASH防止创建隐式有序索引。 -
WITH PARSER *parser_name*此选项仅适用于
FULLTEXT索引。如果全文索引和搜索操作需要特殊处理,则将解析器插件与索引关联起来。InnoDB和MyISAM支持全文解析器插件。如果您有一个关联有全文解析器插件的MyISAM表,您可以使用ALTER TABLE将表转换为InnoDB。有关更多信息,请参见全文解析器插件和编写全文解析器插件。 -
COMMENT '*string*'索引定义可以包括最多 1024 个字符的可选注释。
索引页的
MERGE_THRESHOLD可以通过CREATE INDEX语句的*index_option*COMMENT子句为单个索引进行配置。例如:CREATE TABLE t1 (id INT); CREATE INDEX id_index ON t1 (id) COMMENT 'MERGE_THRESHOLD=40';如果索引页的页面满百分比低于
MERGE_THRESHOLD值,当删除行或更新操作缩短行时,InnoDB会尝试将索引页与相邻的索引页合并。默认的MERGE_THRESHOLD值为 50,这是以前硬编码的值。MERGE_THRESHOLD也可以在索引级别和表级别使用CREATE TABLE和ALTER TABLE语句进行定义。有关更多信息,请参见第 17.8.11 节,“配置索引页合并阈值”。 -
VISIBLE,INVISIBLE指定索引可见性。索引默认可见。不可见索引不会被优化器使用。索引可见性的指定适用于主键以外的索引(显式或隐式)。有关更多信息,请参见第 10.3.12 节,“不可见索引”。
-
ENGINE_ATTRIBUTE和SECONDARY_ENGINE_ATTRIBUTE选项(自 MySQL 8.0.21 起可用)用于指定主要和次要存储引擎的索引属性。这些选项保留供将来使用。允许的值是包含有效
JSON文档的字符串文字或空字符串('')。无效的JSON将被拒绝。CREATE INDEX i1 ON t1 (c1) ENGINE_ATTRIBUTE='{"*key*":"*value*"}';ENGINE_ATTRIBUTE和SECONDARY_ENGINE_ATTRIBUTE值可以重复而不会出错。在这种情况下,将使用最后指定的值。服务器不会检查
ENGINE_ATTRIBUTE和SECONDARY_ENGINE_ATTRIBUTE值,也不会在更改表的存储引擎时清除它们。
表复制和锁定选项
可以提供ALGORITHM和LOCK子句以影响表复制方法和在修改其索引时读写表的并发级别。它们的含义与ALTER TABLE语句相同。有关更多信息,请参见第 15.1.9 节,“ALTER TABLE 语句”。
NDB Cluster 支持使用与标准 MySQL 服务器相同的ALGORITHM=INPLACE语法进行在线操作。有关更多信息,请参见第 25.6.12 节,“NDB Cluster 中的 ALTER TABLE 在线操作”。
15.1.16 CREATE LOGFILE GROUP Statement
原文:
dev.mysql.com/doc/refman/8.0/en/create-logfile-group.html
CREATE LOGFILE GROUP *logfile_group*
ADD UNDOFILE '*undo_file*'
[INITIAL_SIZE [=] *initial_size*]
[UNDO_BUFFER_SIZE [=] *undo_buffer_size*]
[REDO_BUFFER_SIZE [=] *redo_buffer_size*]
[NODEGROUP [=] *nodegroup_id*]
[WAIT]
[COMMENT [=] '*string*']
ENGINE [=] *engine_name*
此语句创建一个名为*logfile_group的新日志文件组,其中包含一个名为'undo_file*'的单个UNDO文件。CREATE LOGFILE GROUP语句只有一个ADD UNDOFILE子句。有关日志文件组命名规则,请参见第 11.2 节,“模式对象名称”。
注意
所有 NDB Cluster 磁盘数据对象共享相同的命名空间。这意味着每个磁盘数据对象必须具有唯一的名称(而不仅仅是给定类型的每个磁盘数据对象)。例如,您不能拥有具有相同名称的表空间和日志文件组,或者具有相同名称的表空间和数据文件。
在任何给定时间,每个 NDB Cluster 实例只能有一个日志文件组。
可选的INITIAL_SIZE参数设置UNDO文件的初始大小;如果未指定,则默认为128M(128 兆字节)。可选的UNDO_BUFFER_SIZE参数设置日志文件组的UNDO缓冲区使用的大小;UNDO_BUFFER_SIZE的默认值为8M(八兆字节);此值不能超过系统内存的数量。这两个参数都以字节为单位指定。您可以选择在这两个参数中的任一个或两个后面跟随一个表示数量级的单个字母缩写,类似于my.cnf中使用的那些字母之一。通常,这是M(表示兆字节)或G(表示千兆字节)中的一个。
用于UNDO_BUFFER_SIZE的内存来自由SharedGlobalMemory数据节点配置参数的值确定的全局池。这包括由InitialLogFileGroup数据节点配置参数的设置隐含的此选项的任何默认值。
UNDO_BUFFER_SIZE的最大允许值为 629145600(600 MB)。
在 32 位系统上,INITIAL_SIZE的最大支持值为 4294967296(4 GB)。 (Bug#29186)
INITIAL_SIZE的最小允许值为 1048576(1 MB)。
ENGINE 选项确定了该日志文件组要使用的存储引擎,engine_name 是存储引擎的名称。在 MySQL 8.0 中,这必须是 NDB(或 NDBCLUSTER)。如果未设置 ENGINE,MySQL 尝试使用由 default_storage_engine 服务器系统变量(以前是 storage_engine)指定的引擎。无论如何,如果未指定引擎为 NDB 或 NDBCLUSTER,CREATE LOGFILE GROUP 语句看起来成功了,但实际上未能创建日志文件组,如下所示:
mysql> CREATE LOGFILE GROUP lg1
-> ADD UNDOFILE 'undo.dat' INITIAL_SIZE = 10M;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> SHOW WARNINGS;
+-------+------+------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+------------------------------------------------------------------------------------------------+
| Error | 1478 | Table storage engine 'InnoDB' does not support the create option 'TABLESPACE or LOGFILE GROUP' |
+-------+------+------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> DROP LOGFILE GROUP lg1 ENGINE = NDB;
ERROR 1529 (HY000): Failed to drop LOGFILE GROUP
mysql> CREATE LOGFILE GROUP lg1
-> ADD UNDOFILE 'undo.dat' INITIAL_SIZE = 10M
-> ENGINE = NDB;
Query OK, 0 rows affected (2.97 sec)
CREATE LOGFILE GROUP 语句在命名非NDB存储引擎时实际上并不会返回错误,而是看起来成功了,这是一个已知问题,我们希望在未来的 NDB Cluster 版本中解决。
REDO_BUFFER_SIZE、NODEGROUP、WAIT 和 COMMENT 被解析但被忽略,在 MySQL 8.0 中没有任何效果。这些选项是为了未来的扩展而设计的。
当与 ENGINE [=] NDB 一起使用时,在每个 Cluster 数据节点上创建一个日志文件组和相关的 UNDO 日志文件。您可以通过查询信息模式 FILES 表来验证 UNDO 文件是否已创建并获取有关它们的信息。例如:
mysql> SELECT LOGFILE_GROUP_NAME, LOGFILE_GROUP_NUMBER, EXTRA
-> FROM INFORMATION_SCHEMA.FILES
-> WHERE FILE_NAME = 'undo_10.dat';
+--------------------+----------------------+----------------+
| LOGFILE_GROUP_NAME | LOGFILE_GROUP_NUMBER | EXTRA |
+--------------------+----------------------+----------------+
| lg_3 | 11 | CLUSTER_NODE=3 |
| lg_3 | 11 | CLUSTER_NODE=4 |
+--------------------+----------------------+----------------+
2 rows in set (0.06 sec)
CREATE LOGFILE GROUP 仅在 NDB Cluster 的 Disk Data 存储中有用。请参阅 第 25.6.11 节,“NDB Cluster Disk Data Tables”。
15.1.17 CREATE PROCEDURE and CREATE FUNCTION Statements
CREATE
[DEFINER = *user*]
PROCEDURE [IF NOT EXISTS] *sp_name* ([*proc_parameter*[,...]])
[*characteristic* ...] *routine_body*
CREATE
[DEFINER = *user*]
FUNCTION [IF NOT EXISTS] *sp_name* ([*func_parameter*[,...]])
RETURNS *type*
[*characteristic* ...] *routine_body*
*proc_parameter*:
[ IN | OUT | INOUT ] *param_name* *type*
*func_parameter*:
*param_name* *type*
*type*:
*Any valid MySQL data type* *characteristic*: {
COMMENT '*string*'
| LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
}
*routine_body*:
*Valid SQL routine statement*
这些语句用于创建存储例程(存储过程或函数)。也就是说,指定的例程会被服务器识别。默认情况下,存储例程与默认数据库关联。要将例程明确关联到特定数据库,请在创建时指定名称为*db_name.sp_name*。
CREATE FUNCTION语句在 MySQL 中还用于支持可加载函数。请参阅 Section 15.7.4.1, “CREATE FUNCTION Statement for Loadable Functions”。可加载函数可以被视为外部存储函数。存储函数与可加载函数共享命名空间。有关服务器解释对不同类型函数引用的规则,请参阅 Section 11.2.5, “Function Name Parsing and Resolution”。
要调用存储过程,请使用CALL语句(参见 Section 15.2.1, “CALL Statement”)。要调用存储函数,请在表达式中引用它。在表达式评估期间,函数会返回一个值。
CREATE PROCEDURE和CREATE FUNCTION需要CREATE ROUTINE权限。如果存在DEFINER子句,则所需的权限取决于*user*值,如 Section 27.6, “Stored Object Access Control”中所讨论的。如果启用了二进制日志记录,则CREATE FUNCTION可能需要SUPER权限,如 Section 27.7, “Stored Program Binary Logging”中所讨论的。
默认情况下,MySQL 自动授予ALTER ROUTINE和EXECUTE权限给例程创建者。这种行为可以通过禁用automatic_sp_privileges系统变量来更改。请参阅 Section 27.2.2, “Stored Routines and MySQL Privileges”。
DEFINER和SQL SECURITY子句指定在例程执行时检查访问权限时要使用的安全上下文,如本节后面所述。
如果例程名称与内置 SQL 函数的名称相同,在定义例程或稍后调用它时,如果名称和后面的括号之间没有空格,将会发生语法错误。因此,避免使用现有 SQL 函数的名称作为自己的存储过程名称。
IGNORE_SPACE SQL 模式适用于内置函数,而不适用于存储过程。无论 IGNORE_SPACE 是否启用,存储过程名称后面都可以有空格。
IF NOT EXISTS 可以防止在已经存在同名例程时出现错误。从 MySQL 8.0.29 开始,CREATE FUNCTION 和 CREATE PROCEDURE 都支持这个选项。
如果同名的内置函数已经存在,尝试使用 CREATE FUNCTION ... IF NOT EXISTS 创建存储函数会成功,并显示警告指示它与本地函数同名;这与执行相同的 CREATE FUNCTION 语句但不指定 IF NOT EXISTS 时没有区别。
如果同名的可加载函数已经存在,使用 IF NOT EXISTS 尝试创建存储函数会成功并显示警告。这与不指定 IF NOT EXISTS 时的情况相同。
更多信息请参阅函数名称解析。
括在括号内的参数列表必须始终存在。如果没有参数,则应使用空参数列表 ()。参数名称不区分大小写。
每个参数默认为 IN 参数。要为参数指定其他方式,请在参数名称之前使用关键字 OUT 或 INOUT。
注意
仅对 PROCEDURE 指定参数为 IN、OUT 或 INOUT 是有效的。对于 FUNCTION,参数始终被视为 IN 参数。
IN 参数将一个值传递给存储过程。存储过程可能会修改该值,但当存储过程返回时,对调用者不可见。OUT 参数将一个值从存储过程传递回调用者。在存储过程内部,其初始值为 NULL,当存储过程返回时,其值对调用者可见。INOUT 参数由调用者初始化,可以被存储过程修改,存储过程所做的任何更改在存储过程返回时对调用者可见。
对于每个OUT或INOUT参数,在调用过程的CALL语句中传递一个用户定义的变量,以便在过程返回时获取其值。如果您从另一个存储过程或函数内部调用该过程,还可以将例程参数或本地例程变量作为OUT或INOUT参数传递。如果您从触发器内部调用该过程,还可以将NEW.*col_name*作为OUT或INOUT参数传递。
有关未处理条件对过程参数的影响的信息,请参见 Section 15.6.7.8, “Condition Handling and OUT or INOUT Parameters”。
例程参数不能在例程内准备的语句中引用;请参见 Section 27.8, “Restrictions on Stored Programs”。
以下示例显示了一个简单的存储过程,根据国家代码计算出现在world数据库的city表中的该国家的城市数量。国家代码使用IN参数传递,并使用OUT参数返回城市计数:
mysql> delimiter //
mysql> CREATE PROCEDURE citycount (IN country CHAR(3), OUT cities INT)
BEGIN
SELECT COUNT(*) INTO cities FROM world.city
WHERE CountryCode = country;
END//
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql> CALL citycount('JPN', @cities); -- cities in Japan
Query OK, 1 row affected (0.00 sec)
mysql> SELECT @cities;
+---------+
| @cities |
+---------+
| 248 |
+---------+
1 row in set (0.00 sec)
mysql> CALL citycount('FRA', @cities); -- cities in France
Query OK, 1 row affected (0.00 sec)
mysql> SELECT @cities;
+---------+
| @cities |
+---------+
| 40 |
+---------+
1 row in set (0.00 sec)
该示例使用mysql客户端delimiter命令在定义过程时将语句定界符从;更改为//。这样,在过程体中使用的;定界符将被传递到服务器而不是被mysql本身解释。请参见 Section 27.1, “Defining Stored Programs”。
RETURNS子句只能为FUNCTION指定,对于FUNCTION是强制的。它指示函数的返回类型,函数体必须包含一个RETURN *value*语句。如果RETURN语句返回不同类型的值,则将该值强制转换为正确的类型。例如,如果函数在RETURNS子句中指定了ENUM或SET值,但RETURN语句返回一个整数,则从函数返回的值是相应ENUM成员或SET成员的字符串。
以下示例函数接受一个参数,使用 SQL 函数执行操作,并返回结果。在这种情况下,不需要使用delimiter,因为函数定义不包含内部的;语句定界符:
mysql> CREATE FUNCTION hello (s CHAR(20))
mysql> RETURNS CHAR(50) DETERMINISTIC
RETURN CONCAT('Hello, ',s,'!');
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world! |
+----------------+
1 row in set (0.00 sec)
参数类型和函数返回类型可以声明为任何有效的数据类型。如果在CHARACTER SET规范之前使用COLLATE属性,则可以使用它。
routine_body 包含一个有效的 SQL 例程语句。这可以是一个简单的语句,比如SELECT或INSERT,或者使用BEGIN和END编写的复合语句。复合语句可以包含声明、循环和其他控制结构语句。这些语句的语法在第 15.6 节“复合语句语法”中描述。在实践中,存储函数往往使用复合语句,除非主体由单个RETURN语句组成。
MySQL 允许例程包含 DDL 语句,比如CREATE和DROP。MySQL 也允许存储过程(但不允许存储函数)包含 SQL 事务语句,比如COMMIT。存储函数不能包含执行显式或隐式提交或回滚的语句。SQL 标准不要求支持这些语句,它规定每个 DBMS 供应商可以决定是否允许它们。
返回结果集的语句可以在存储过程中使用,但不能在存储函数中使用。这个禁令包括没有INTO *var_list*子句的SELECT语句以及其他语句,比如SHOW、EXPLAIN和CHECK TABLE。对于在函数定义时可以确定返回结果集的语句,会出现Not allowed to return a result set from a function错误(ER_SP_NO_RETSET)。对于只能在运行时确定返回结果集的语句,会出现PROCEDURE %s can't return a result set in the given context错误(ER_SP_BADSELECT)。
存储例程中不允许使用USE语句。当调用例程时,会执行一个隐式的USE *db_name*(在例程终止时撤消)。这会导致例程在执行时具有给定的默认数据库。对于例程默认数据库之外的数据库中的对象的引用应该使用适当的数据库名称进行限定。
有关存储例程中不允许的语句的更多信息,请参见第 27.8 节“存储程序的限制”。
有关如何从具有 MySQL 接口的语言编写的程序中调用存储过程的信息,请参见第 15.2.1 节“CALL 语句”。
MySQL 在创建或更改例程时存储 sql_mode 系统变量设置,并始终以此设置执行例程,不管例程开始执行时当前服务器 SQL 模式如何。
从调用者的 SQL 模式切换到例程的 SQL 模式发生在参数评估和将结果值分配给例程参数之后。如果在严格 SQL 模式下定义例程但在非严格模式下调用它,则参数分配给例程参数不会在严格模式下进行。如果要求传递给例程的表达式在严格 SQL 模式下分配,应该在调用例程时启用严格模式。
COMMENT 特性是 MySQL 的扩展,可用于描述存储例程。这些信息会被 SHOW CREATE PROCEDURE 和 SHOW CREATE FUNCTION 语句显示。
LANGUAGE 特性表示例程所编写的语言。服务器会忽略此特性;仅支持 SQL 例程。
如果一个例程对于相同的输入参数总是产生相同的结果,则被认为是“确定性的”,否则是“非确定性的”。如果例程定义中既没有 DETERMINISTIC 也没有 NOT DETERMINISTIC,则默认为 NOT DETERMINISTIC。要声明一个函数是确定性的,必须明确指定 DETERMINISTIC。
对例程性质的评估基于创建者的“诚实度”:MySQL 不会检查声明为 DETERMINISTIC 的例程是否不包含产生非确定性结果的语句。然而,错误声明例程可能会影响结果或性能。将一个非确定性例程声明为 DETERMINISTIC 可能会导致意外结果,因为优化器会做出错误的执行计划选择。将一个确定性例程声明为 NONDETERMINISTIC 可能会降低性能,因为可用的优化不会被使用。
如果启用了二进制日志记录,DETERMINISTIC 特性会影响 MySQL 接受哪些例程定义。参见 第 27.7 节,“存储程序二进制日志记录”。
包含NOW()函数(或其同义词)或RAND()的例程是不确定性的,但可能仍然是复制安全的。对于NOW(),二进制日志包括时间戳并正确复制。RAND()只要在例程执行过程中仅调用一次,也会正确复制。(您可以将例程执行时间戳和随机数种子视为在源和副本上相同的隐式输入。)
几个特性提供有关例程使用数据性质的信息。在 MySQL 中,这些特性仅供参考。服务器不使用它们来限制例程允许执行的语句类型。
-
CONTAINS SQL表示例程不包含读取或写入数据的语句。如果没有明确给出这些特性中的任何一个,则这是默认值。此类语句的示例是SET @x = 1或DO RELEASE_LOCK('abc'),它们执行但既不读取也不写入数据。 -
NO SQL表示例程不包含 SQL 语句。 -
READS SQL DATA表示例程包含读取数据的语句(例如,SELECT),但不包含写入数据的语句。 -
MODIFIES SQL DATA表示例程包含可能写入数据的语句(例如,INSERT或DELETE)。
SQL SECURITY特性可以是DEFINER或INVOKER,用于指定安全上下文;也就是说,例程是使用例程DEFINER子句中命名的帐户的权限执行,还是由调用者执行。此帐户必须具有访问与例程关联的数据库的权限。默认值为DEFINER。调用例程的用户必须具有执行权限,以及如果例程在定义者安全上下文中执行,则DEFINER帐户也必须具有执行权限。
DEFINER子句指定了在具有SQL SECURITY DEFINER特性的例程执行时检查访问权限时要使用的 MySQL 帐户。
如果存在DEFINER子句,则*user值应为指定为'*user_name*'@'*host_name*、CURRENT_USER或CURRENT_USER()的 MySQL 帐户。允许的user*值取决于您拥有的权限,如第 27.6 节“存储对象访问控制”中所讨论的。此外,请参阅该部分以获取有关存储例程安全性的其他信息。
如果省略DEFINER子句,则默认的定义者是执行CREATE PROCEDURE或CREATE FUNCTION语句的用户。这与明确指定DEFINER = CURRENT_USER相同。
在具有SQL SECURITY DEFINER特性定义的存储例程体内,CURRENT_USER函数返回例程的DEFINER值。有关存储例程中用户审计的信息,请参见第 8.2.23 节,“基于 SQL 的帐户活动审计”。
考虑以下过程,该过程显示mysql.user系统表中列出的 MySQL 帐户数量的计数:
CREATE DEFINER = 'admin'@'localhost' PROCEDURE account_count()
BEGIN
SELECT 'Number of accounts:', COUNT(*) FROM mysql.user;
END;
无论哪个用户定义该过程,该过程都被分配了一个'admin'@'localhost'的DEFINER帐户。它以该帐户的权限执行,无论哪个用户调用它(因为默认的安全特性是DEFINER)。该过程的成功或失败取决于调用者是否具有EXECUTE权限以及'admin'@'localhost'是否具有mysql.user表的SELECT权限。
现在假设该过程使用SQL SECURITY INVOKER特性定义:
CREATE DEFINER = 'admin'@'localhost' PROCEDURE account_count()
SQL SECURITY INVOKER
BEGIN
SELECT 'Number of accounts:', COUNT(*) FROM mysql.user;
END;
该过程仍然具有'admin'@'localhost'的DEFINER,但在这种情况下,它以调用用户的权限执行。因此,该过程的成功或失败取决于调用者是否具有EXECUTE权限以及mysql.user表的SELECT权限。
默认情况下,当具有SQL SECURITY DEFINER特性的例程被执行时,MySQL 服务器不会为DEFINER子句中命名的 MySQL 帐户设置任何活动角色,只有默认角色。例外情况是如果启用了activate_all_roles_on_login系统变量,此时 MySQL 服务器会设置授予DEFINER用户的所有角色,包括强制角色。因此,默认情况下,在发出CREATE PROCEDURE或CREATE FUNCTION语句时,不会检查通过角色授予的任何权限。对于存储程序,如果执行应该使用与默认不同的角色,则程序体可以执行SET ROLE来激活所需的角色。这必须谨慎进行,因为分配给角色的权限可能会更改。
服务器处理例程参数、使用DECLARE创建的本地例程变量,或函数返回值的数据类型如下:
-
分配会检查数据类型不匹配和溢出。转换和溢出问题会导致警告,或在严格的 SQL 模式下出现错误。
-
只能分配标量值。例如,
SET x = (SELECT 1, 2)这样的语句是无效的。 -
对于字符数据类型,如果声明中包含
CHARACTER SET,则使用指定的字符集及其默认排序规则。如果还存在COLLATE属性,则使用该排序规则而不是默认排序规则。如果没有
CHARACTER SET和COLLATE,则在例程创建时生效的数据库字符集和排序规则会被使用。为了避免服务器使用数据库字符集和排序规则,请为字符数据参数提供明确的CHARACTER SET和COLLATE属性。如果更改数据库默认字符集或排序规则,则必须删除并重新创建要使用新数据库默认值的存储例程。
数据库字符集和排序规则由
character_set_database和collation_database系统变量的值给出。更多信息,请参见 Section 12.3.3, “Database Character Set and Collation”。
15.1.18 CREATE SERVER 语句
CREATE SERVER *server_name*
FOREIGN DATA WRAPPER *wrapper_name*
OPTIONS (*option* [, *option*] ...)
*option*: {
HOST *character-literal*
| DATABASE *character-literal*
| USER *character-literal*
| PASSWORD *character-literal*
| SOCKET *character-literal*
| OWNER *character-literal*
| PORT *numeric-literal*
}
这个语句创建了一个用于FEDERATED存储引擎的服务器定义。CREATE SERVER语句在mysql数据库的servers表中创建了一行新记录。此语句需要SUPER权限。
*server_name*应该是对服务器的唯一引用。服务器定义在服务器的范围内是全局的,不可能将服务器定义限定到特定数据库。*server_name*最大长度为 64 个字符(超过 64 个字符的名称会被静默截断),且不区分大小写。您可以将名称指定为带引号的字符串。
*wrapper_name*是一个标识符,可以用单引号引起来。
对于每个*option*,您必须指定字符文字或数字文字。字符文字为 UTF-8 编码,支持最大长度为 64 个字符,默认为空字符串。字符串文字会被静默截断为 64 个字符。数字文字必须是 0 到 9999 之间的数字,默认值为 0。
注意
OWNER选项目前未应用,对创建的服务器连接的所有权或操作没有影响。
CREATE SERVER语句在mysql.servers表中创建一个条目,以后可以在创建FEDERATED表时与CREATE TABLE语句一起使用。您指定的选项用于填充mysql.servers表中的列。表列包括Server_name、Host、Db、Username、Password、Port和Socket。
例如:
CREATE SERVER s
FOREIGN DATA WRAPPER mysql
OPTIONS (USER 'Remote', HOST '198.51.100.106', DATABASE 'test');
一定要指定建立与服务器连接所需的所有选项。用户名、主机名和数据库名是必需的。可能还需要其他选项,比如密码。
存储在表中的数据可在创建到FEDERATED表的连接时使用:
CREATE TABLE t (s1 INT) ENGINE=FEDERATED CONNECTION='s';
欲了解更多信息,请参阅第 18.8 节,“FEDERATED 存储引擎”。
CREATE SERVER会导致隐式提交。请参阅第 15.3.3 节,“导致隐式提交的语句”。
无论使用的日志格式如何,CREATE SERVER都不会写入二进制日志。
15.1.19 CREATE SPATIAL REFERENCE SYSTEM 语句
原文:
dev.mysql.com/doc/refman/8.0/en/create-spatial-reference-system.html
CREATE OR REPLACE SPATIAL REFERENCE SYSTEM
*srid* *srs_attribute* ...
CREATE SPATIAL REFERENCE SYSTEM
[IF NOT EXISTS]
*srid* *srs_attribute* ...
*srs_attribute*: {
NAME '*srs_name*'
| DEFINITION '*definition*'
| ORGANIZATION '*org_name*' IDENTIFIED BY *org_id*
| DESCRIPTION '*description*'
}
*srid*, *org_id*: *32-bit unsigned integer*
此语句创建一个空间参考系统(SRS)定义,并将其存储在数据字典中。它需要SUPER权限。生成的数据字典条目可以使用INFORMATION_SCHEMA ST_SPATIAL_REFERENCE_SYSTEMS表进行检查。
SRID 值必须是唯一的,因此如果未指定OR REPLACE或IF NOT EXISTS,则如果具有给定*srid*值的 SRS 定义已经存在,则会发生错误。
使用CREATE OR REPLACE语法,任何具有相同 SRID 值的现有 SRS 定义都将被替换,除非该 SRID 值被现有表中的某列使用。在这种情况下,将会发生错误。例如:
mysql> CREATE OR REPLACE SPATIAL REFERENCE SYSTEM 4326 ...;
ERROR 3716 (SR005): Can't modify SRID 4326\. There is at
least one column depending on it.
要确定哪些列使用了 SRID,请使用以下查询,将 4326 替换为您要创建的定义的 SRID:
SELECT * FROM INFORMATION_SCHEMA.ST_GEOMETRY_COLUMNS WHERE SRS_ID=4326;
使用CREATE ... IF NOT EXISTS语法,任何具有相同 SRID 值的现有 SRS 定义都会导致新定义被忽略,并发出警告。
SRID 值必须在 32 位无符号整数范围内,具有以下限制:
-
SRID 0 是一个有效的 SRID,但不能与
CREATE SPATIAL REFERENCE SYSTEM一起使用。 -
如果值在保留的 SRID 范围内,将会发出警告。保留范围为[0, 32767](由 EPSG 保留),[60,000,000, 69,999,999](由 EPSG 保留)和[2,000,000,000, 2,147,483,647](由 MySQL 保留)。EPSG 代表欧洲石油测量组。
-
用户不应该使用保留范围内的 SRID 创建 SRS。这样做会导致 SRID 与 MySQL 分发的未来 SRS 定义发生冲突,结果是新的系统提供的 SRS 未安装用于 MySQL 升级,或者用户定义的 SRS 被覆盖。
语句的属性必须满足以下条件:
-
属性可以以任何顺序给出,但不能重复给出任何属性。
-
NAME和DEFINITION属性是必需的。 -
NAME*srs_name*属性值必须是唯一的。ORGANIZATION*org_name和org_id*属性值的组合必须是唯一的。 -
NAME*srs_name*属性值和ORGANIZATION*org_name*属性值不能为空,也不能以空格开始或结束。 -
属性规范中的字符串值不能包含控制字符,包括换行符。
-
以下表显示了字符串属性值的最大长度。
表 15.6 CREATE SPATIAL REFERENCE SYSTEM 属性长度
属性 最大长度(字符) NAME80 DEFINITION4096 ORGANIZATION256 DESCRIPTION2048
这里是一个 CREATE SPATIAL REFERENCE SYSTEM 语句的示例。DEFINITION 值被重新格式化为多行以提高可读性。(为了语句合法,实际上值必须在单行上给出。)
CREATE SPATIAL REFERENCE SYSTEM 4120
NAME 'Greek'
ORGANIZATION 'EPSG' IDENTIFIED BY 4120
DEFINITION
'GEOGCS["Greek",DATUM["Greek",SPHEROID["Bessel 1841",
6377397.155,299.1528128,AUTHORITY["EPSG","7004"]],
AUTHORITY["EPSG","6120"]],PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],UNIT["degree",0.017453292519943278,
AUTHORITY["EPSG","9122"]],AXIS["Lat",NORTH],AXIS["Lon",EAST],
AUTHORITY["EPSG","4120"]]';
SRS 定义的语法基于 OpenGIS Implementation Specification: Coordinate Transformation Services,修订版 1.00,OGC 01-009,2001 年 1 月 12 日,第 7.2 节中定义的语法。该规范可在 www.opengeospatial.org/standards/ct 上找到。
MySQL 将这些更改纳入规范中:
-
只有
<horz cs>产生规则被实现(即地理和投影 SRSs)。 -
<authority>子句对于<parameter>是可选的,这样可以通过权威而不是名称来识别投影参数。 -
规范中并未要求在
GEOGCS空间参考系统定义中使AXIS子句成为必需的。然而,如果没有AXIS子句,MySQL 无法确定一个定义中的坐标轴是按纬度-经度顺序还是经度-纬度顺序。MySQL 强制要求每个GEOGCS定义必须包括两个AXIS子句。一个必须是NORTH或SOUTH,另一个是EAST或WEST。AXIS子句的顺序决定了定义中的坐标轴是按纬度-经度顺序还是经度-纬度顺序。 -
SRS 定义不得包含换行符。
如果 SRS 定义为投影指定了权威代码(建议这样做),则如果定义缺少必需参数,将会发生错误。在这种情况下,错误消息会指出问题所在。MySQL 支持的投影方法和必需参数显示在 Table 15.7, “Supported Spatial Reference System Projection Methods” 和 Table 15.8, “Spatial Reference System Projection Parameters” 中。
有关在 MySQL 中编写 SRS 定义的更多信息,请参阅 MySQL 8.0 中的地理空间参考系统 和 MySQL 8.0 中的投影空间参考系统
下表显示了 MySQL 支持的投影方法。MySQL 允许未知的投影方法,但无法检查必需参数的定义,也无法将空间数据转换为未知的投影或从未知的投影转换。有关每种投影如何工作的详细解释,包括公式,请参阅EPSG 指南 7-2。
表 15.7 支持的空间参考系统投影方法
| EPSG 代码 | 投影名称 | 必需参数(EPSG 代码) |
|---|---|---|
| 1024 | 流行的可视化伪墨卡托 | 8801, 8802, 8806, 8807 |
| 1027 | 兰伯特等面积圆锥(球形) | 8801, 8802, 8806, 8807 |
| 1028 | 等距圆柱 | 8823, 8802, 8806, 8807 |
| 1029 | 等距圆柱(球形) | 8823, 8802, 8806, 8807 |
| 1041 | 克罗瓦克(北向) | 8811, 8833, 1036, 8818, 8819, 8806, 8807 |
| 1042 | 修改后的克罗瓦克 | 8811, 8833, 1036, 8818, 8819, 8806, 8807, 8617, 8618, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035 |
| 1043 | 修改后的克罗瓦克(北向) | 8811, 8833, 1036, 8818, 8819, 8806, 8807, 8617, 8618, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035 |
| 1051 | 兰伯特圆锥等积(2SP 密歇根) | 8821, 8822, 8823, 8824, 8826, 8827, 1038 |
| 1052 | 哥伦比亚城市 | 8801, 8802, 8806, 8807, 1039 |
| 9801 | 兰伯特圆锥等积(1SP) | 8801, 8802, 8805, 8806, 8807 |
| 9802 | 兰伯特圆锥等积(2SP) | 8821, 8822, 8823, 8824, 8826, 8827 |
| 9803 | 兰伯特圆锥等积(2SP 比利时) | 8821, 8822, 8823, 8824, 8826, 8827 |
| 9804 | 墨卡托(变种 A) | 8801, 8802, 8805, 8806, 8807 |
| 9805 | 墨卡托(变种 B) | 8823, 8802, 8806, 8807 |
| 9806 | 卡西尼-索德纳 | 8801, 8802, 8806, 8807 |
| 9807 | 横轴墨卡托 | 8801, 8802, 8805, 8806, 8807 |
| 9808 | 横轴墨卡托(南向) | 8801, 8802, 8805, 8806, 8807 |
| 9809 | 斜方位立体投影 | 8801, 8802, 8805, 8806, 8807 |
| 9810 | 极地立体投影(变种 A) | 8801, 8802, 8805, 8806, 8807 |
| 9811 | 新西兰地图网格 | 8801, 8802, 8806, 8807 |
| 9812 | 侧面斜方位墨卡托(变种 A) | 8811, 8812, 8813, 8814, 8815, 8806, 8807 |
| 9813 | 拉博德斜方位墨卡托 | 8811, 8812, 8813, 8815, 8806, 8807 |
| 9815 | 侧面斜方位墨卡托(变种 B) | 8811, 8812, 8813, 8814, 8815, 8816, 8817 |
| 9816 | 突尼斯矿业网格 | 8821, 8822, 8826, 8827 |
| 9817 | 兰伯特圆锥近等积 | 8801, 8802, 8805, 8806, 8807 |
| 9818 | 美国多锥形 | 8801, 8802, 8806, 8807 |
| 9819 | 克罗瓦克 | 8811, 8833, 1036, 8818, 8819, 8806, 8807 |
| 9820 | 兰伯特等面积圆锥 | 8801, 8802, 8806, 8807 |
| 9822 | 阿尔伯斯等面积 | 8821, 8822, 8823, 8824, 8826, 8827 |
| 9824 | 横轴墨卡托分带网格系统 | 8801, 8830, 8831, 8805, 8806, 8807 |
| 9826 | 兰伯特圆锥等积(西向) | 8801, 8802, 8805, 8806, 8807 |
| 9828 | 博恩(南向) | 8801, 8802, 8806, 8807 |
| 9829 | 极射赤面投影(变种 B) | 8832, 8833, 8806, 8807 |
| 9830 | 极射赤面投影(变种 C) | 8832, 8833, 8826, 8827 |
| 9831 | 关岛投影 | 8801, 8802, 8806, 8807 |
| 9832 | 修改的等距方位投影 | 8801, 8802, 8806, 8807 |
| 9833 | 双曲卡西尼-索尔德投影 | 8801, 8802, 8806, 8807 |
| 9834 | 兰伯特圆柱等面积投影(球面) | 8823, 8802, 8806, 8807 |
| 9835 | 兰伯特圆柱等面积投影 | 8823, 8802, 8806, 8807 |
| EPSG 代码 | 投影名称 | 强制参数(EPSG 代码) |
下表显示了 MySQL 可识别的投影参数。识别主要通过授权代码进行。如果没有授权代码,MySQL 将回退到对参数名称的不区分大小写的字符串匹配。有关每个参数的详细信息,请通��� EPSG 在线注册表 的代码查找。
表 15.8 空间参考系统投影参数
| EPSG 代码 | 回退名称(MySQL 可识别) | EPSG 名称 |
|---|---|---|
| 1026 | c1 | C1 |
| 1027 | c2 | C2 |
| 1028 | c3 | C3 |
| 1029 | c4 | C4 |
| 1030 | c5 | C5 |
| 1031 | c6 | C6 |
| 1032 | c7 | C7 |
| 1033 | c8 | C8 |
| 1034 | c9 | C9 |
| 1035 | c10 | C10 |
| 1036 | 方位角 | 锥轴的共纬度 |
| 1038 | 椭球体比例因子 | 椭球体比例因子 |
| 1039 | 投影平面原点高度 | 投影平面原点高度 |
| 8617 | evaluation_point_ordinate_1 | 评估点纵坐标 1 |
| 8618 | evaluation_point_ordinate_2 | 评估点纵坐标 2 |
| 8801 | 原点纬度 | 自然原点纬度 |
| 8802 | 中央经线 | 自然原点经度 |
| 8805 | 比例因子 | 自然原点的比例因子 |
| 8806 | 东偏移 | 伪东移距离 |
| 8807 | 北偏移 | 伪北移距离 |
| 8811 | 中心纬度 | 投影中心纬度 |
| 8812 | 中心经度 | 投影中心经度 |
| 8813 | 方位角 | 初始线方位角 |
| 8814 | 矫正网格角度 | 从矫正到斜网格的角度 |
| 8815 | 比例因子 | 初始线上的比例因子 |
| 8816 | 东偏移 | 投影中心的东移距离 |
| 8817 | 北偏移 | 投影中心的北移距离 |
| 8818 | 伪标准纬线 1 | 伪标准纬线纬度 |
| 8819 | 比例因子 | 伪标准纬线上的比例因子 |
| 8821 | 原点纬度 | 伪原点纬度 |
| 8822 | central_meridian | 伪原点经度 |
| 8823 | 第 1 标准纬线 | 第 1 标准纬线纬度 |
| 8824 | 第 2 标准纬线 | 第 2 标准纬线纬度 |
| 8826 | 东偏移 | 伪原点的东移距离 |
| 8827 | 北偏移 | 伪原点的北移距离 |
| 8830 | 初始经度 | 初始经度 |
| 8831 | 分带宽度 | 分带宽度 |
| 8832 | 标准纬线 | 标准纬线纬度 |
| 8833 | 中央经度 | 起始经度 |
| EPSG 代码 | 回退名称(MySQL 可识别) | EPSG 名称 |