MySQL8-中文参考-二十五-
MySQL8 中文参考(二十五)
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_SUBSTITUTION
SQL 模式设置的影响,如 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_name
ibfk”开头的用户定义的外键约束名称,以反映新表名称。InnoDB
将以字符串“tbl_name
ibfk”开头的外键约束名称解释为内部生成的名称。
在 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_name
chk”开头的CHECK
约束名称,以反映新表名。MySQL 将以字符串“tbl_name
chk”开头的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”。
原文:
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 存储引擎的索引类型
存储引擎 允许的索引类型 InnoDB
BTREE
MyISAM
BTREE
MEMORY
/HEAP
HASH
,BTREE
NDB
HASH
,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
是 是 索引 索引 FULLTEXT
N/A 是 是 表 表 SPATIAL
N/A 否 否 N/A N/A 表 15.3 MyISAM 存储引擎索引特性
索引类别 索引类型 存储 NULL 值 允���多个 NULL 值 IS NULL 扫描类型 IS NOT NULL 扫描类型 主键 BTREE
否 否 N/A N/A 独特 BTREE
是 是 索引 索引 键 BTREE
是 是 索引 索引 FULLTEXT
N/A 是 是 表 表 SPATIAL
N/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 属性长度
属性 最大长度(字符) NAME
80 DEFINITION
4096 ORGANIZATION
256 DESCRIPTION
2048
这里是一个 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 名称 |
15.1.20 CREATE TABLE 语句
15.1.20.1 CREATE TABLE 创建的文件
15.1.20.2 CREATE TEMPORARY TABLE 语句
15.1.20.3 CREATE TABLE ... LIKE 语句
15.1.20.4 CREATE TABLE ... SELECT 语句
15.1.20.5 外键约束
15.1.20.6 CHECK 约束
15.1.20.7 隐式列规范更改
15.1.20.8 CREATE TABLE 和生成列
15.1.20.9 二级索引和生成列
15.1.20.10 隐式列
15.1.20.11 生成的隐式主键
15.1.20.12 设置 NDB 注释选项
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] *tbl_name*
(*create_definition*,...)
[*table_options*]
[*partition_options*]
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] *tbl_name*
[(*create_definition*,...)]
[*table_options*]
[*partition_options*]
[IGNORE | REPLACE]
[AS] *query_expression*
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] *tbl_name*
{ LIKE *old_tbl_name* | (LIKE *old_tbl_name*) }
*create_definition*: {
*col_name* *column_definition*
| {INDEX | KEY} [*index_name*] [*index_type*] (*key_part*,...)
[*index_option*] ...
| {FULLTEXT | SPATIAL} [INDEX | KEY] [*index_name*] (*key_part*,...)
[*index_option*] ...
| [CONSTRAINT [*symbol*]] PRIMARY KEY
[*index_type*] (*key_part*,...)
[*index_option*] ...
| [CONSTRAINT [*symbol*]] UNIQUE [INDEX | KEY]
[*index_name*] [*index_type*] (*key_part*,...)
[*index_option*] ...
| [CONSTRAINT [*symbol*]] FOREIGN KEY
[*index_name*] (*col_name*,...)
*reference_definition*
| *check_constraint_definition*
}
*column_definition*: {
*data_type* [NOT NULL | NULL] [DEFAULT {*literal* | (*expr*)} ]
[VISIBLE | INVISIBLE]
[AUTO_INCREMENT] [UNIQUE [KEY]] [[PRIMARY] KEY]
[COMMENT '*string*']
[COLLATE *collation_name*]
[COLUMN_FORMAT {FIXED | DYNAMIC | DEFAULT}]
[ENGINE_ATTRIBUTE [=] '*string*']
[SECONDARY_ENGINE_ATTRIBUTE [=] '*string*']
[STORAGE {DISK | MEMORY}]
[*reference_definition*]
[*check_constraint_definition*]
| *data_type*
[COLLATE *collation_name*]
[GENERATED ALWAYS] AS (*expr*)
[VIRTUAL | STORED] [NOT NULL | NULL]
[VISIBLE | INVISIBLE]
[UNIQUE [KEY]] [[PRIMARY] KEY]
[COMMENT '*string*']
[*reference_definition*]
[*check_constraint_definition*]
}
*data_type*:
(see Chapter 13, Data Types)
*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}
|ENGINE_ATTRIBUTE [=] '*string*'
|SECONDARY_ENGINE_ATTRIBUTE [=] '*string*'
}
*check_constraint_definition*:
[CONSTRAINT [*symbol*]] CHECK (*expr*) [[NOT] ENFORCED]
*reference_definition*:
REFERENCES *tbl_name* (*key_part*,...)
[MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
[ON DELETE *reference_option*]
[ON UPDATE *reference_option*]
*reference_option*:
RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT
*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}
| START TRANSACTION
| SECONDARY_ENGINE_ATTRIBUTE [=] '*string*'
| STATS_AUTO_RECALC [=] {DEFAULT | 0 | 1}
| STATS_PERSISTENT [=] {DEFAULT | 0 | 1}
| STATS_SAMPLE_PAGES [=] *value*
| *tablespace_option*
| UNION [=] (*tbl_name*[,*tbl_name*]...)
}
*partition_options*:
PARTITION BY
{ [LINEAR] HASH(*expr*)
| [LINEAR] KEY [ALGORITHM={1 | 2}] (*column_list*)
| RANGE{(*expr*) | COLUMNS(*column_list*)}
| LIST{(*expr*) | COLUMNS(*column_list*)} }
[PARTITIONS *num*]
[SUBPARTITION BY
{ [LINEAR] HASH(*expr*)
| [LINEAR] KEY [ALGORITHM={1 | 2}] (*column_list*) }
[SUBPARTITIONS *num*]
]
[(*partition_definition* [, *partition_definition*] ...)]
*partition_definition*:
PARTITION *partition_name*
[VALUES
{LESS THAN {(*expr* | *value_list*) | MAXVALUE}
|
IN (*value_list*)}]
[[STORAGE] ENGINE [=] *engine_name*]
[COMMENT [=] '*string*' ]
[DATA DIRECTORY [=] '*data_dir*']
[INDEX DIRECTORY [=] '*index_dir*']
[MAX_ROWS [=] *max_number_of_rows*]
[MIN_ROWS [=] *min_number_of_rows*]
[TABLESPACE [=] tablespace_name]
[(*subpartition_definition* [, *subpartition_definition*] ...)]
*subpartition_definition*:
SUBPARTITION *logical_name*
[[STORAGE] ENGINE [=] *engine_name*]
[COMMENT [=] '*string*' ]
[DATA DIRECTORY [=] '*data_dir*']
[INDEX DIRECTORY [=] '*index_dir*']
[MAX_ROWS [=] *max_number_of_rows*]
[MIN_ROWS [=] *min_number_of_rows*]
[TABLESPACE [=] tablespace_name]
*tablespace_option*:
TABLESPACE *tablespace_name* [STORAGE DISK]
| [TABLESPACE *tablespace_name*] STORAGE MEMORY
*query_expression:*
SELECT ... (*Some valid select or union statement*)
CREATE TABLE
创建具有给定名称的表。您必须对表具有 CREATE
权限。
默认情况下,表在默认数据库中使用 InnoDB
存储引擎创建。如果表已存在、没有默认数据库或数据库不存在,则会出现错误。
MySQL 对表的数量没有限制。底层文件系统可能对代表表的文件数量有限制。各个存储引擎可能会施加特定于引擎的约束。InnoDB
允许最多 40 亿个表。
有关表的物理表示信息,请参阅 第 15.1.20.1 节“CREATE TABLE 创建的文件”.
CREATE TABLE
语句有几个方面,在本节的以下主题中描述:
-
表名
-
临时表
-
表克隆和复制
-
列数据类型和属性
-
索引、外键和 CHECK 约束
-
表选项
-
表分区
表名
-
*
tbl_name*
表名可以指定为
db_name.tbl_name
,以在特定数据库中创建表。无论是否存在默认数据库,都可以使用此方法,假设数据库存在。如果使用带引号的标识符,请分别引用数据库和表名。例如,写成mydb`.`mytbl
,而不是mydb.mytbl
。可接受的表名规则在第 11.2 节,“模式对象名称”中给出。
-
IF NOT EXISTS
如果表存在,则防止出现错误。但是,并没有验证现有表的结构是否与
CREATE TABLE
语句指示的结构完全相同。
临时表
在创建表时,可以使用 TEMPORARY
关键字。TEMPORARY
表仅在当前会话中可见,并在会话关闭时自动删除。有关更多信息,请参见第 15.1.20.2 节,“CREATE TEMPORARY TABLE 语句”。
表克隆和复制
-
LIKE
使用
CREATE TABLE ... LIKE
根据另一个表的定义创建一个空表,包括原始表中定义的任何列属性和索引:CREATE TABLE *new_tbl* LIKE *orig_tbl*;
更多信息,请参见第 15.1.20.3 节,“CREATE TABLE ... LIKE 语句”。
-
[AS] *
query_expression*
要从一个表创建另一个表,请在
CREATE TABLE
语句末尾添加一个SELECT
语句:CREATE TABLE *new_tbl* AS SELECT * FROM *orig_tbl*;
更多信息,请参见第 15.1.20.4 节,“CREATE TABLE ... SELECT 语句”。
-
IGNORE | REPLACE
IGNORE
和REPLACE
选项指示在使用SELECT
语句复制表时如何处理重复唯一键值的行。更多信息,请参见第 15.1.20.4 节,“CREATE TABLE ... SELECT 语句”。
列数据类型和属性
每个表的列有一个硬限制为 4096 列,但对于给定表,有效最大值可能会更少,并取决于第 10.4.7 节,“表列计数和行大小限制”中讨论的因素。
-
*
data_type*
data_type
代表列定义中的数据类型。有关指定列数据类型的语法以及每种类型属性的详细描述,请参见第十三章,数据类型。-
一些属性不适用于所有数据类型。
AUTO_INCREMENT
仅适用于整数和浮点类型。在 MySQL 8.0.17 中,使用AUTO_INCREMENT
与FLOAT
或DOUBLE
列已被弃用;预计在未来的 MySQL 版本中将删除对其的支持。在 MySQL 8.0.13 之前,
DEFAULT
不适用于BLOB
,TEXT
,GEOMETRY
和JSON
类型。 -
字符数据类型(
CHAR
,VARCHAR
,TEXT
,ENUM
,SET
和任何同义词)可以包括CHARACTER SET
来指定列的字符集。CHARSET
是CHARACTER SET
的同义词。可以使用COLLATE
属性指定字符集的排序规则,以及其他任何属性。有关详细信息,请参见 第十二章,字符集、排序规则、Unicode。示例:CREATE TABLE t (c CHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin);
MySQL 8.0 解释字符列定义中的长度规范为字符。
BINARY
和VARBINARY
的长度以字节为单位。 -
对于
CHAR
、VARCHAR
、BINARY
和VARBINARY
列,可以创建仅使用列值前导部分的索引,使用*
col_name*(*
length*)
语法指定索引前缀长度。BLOB
和TEXT
列也可以被索引,但必须给出前缀长度。对于非二进制字符串类型,前缀长度以字符为单位,对于二进制字符串类型,前缀长度以字节为单位。也就是说,对于CHAR
、VARCHAR
和TEXT
列,索引条目由每个列值的前length
个字符组成,对于BINARY
、VARBINARY
和BLOB
列,索引条目由每个列值的前length
个字节组成。像这样仅对列值前缀进行索引可以使索引文件变得更小。有关索引前缀的更多信息,请参见 第 15.1.15 节,“CREATE INDEX 语句”。仅
InnoDB
和MyISAM
存储引擎支持对BLOB
和TEXT
列进行索引。例如:CREATE TABLE test (blob_col BLOB, INDEX(blob_col(10)));
如果指定的索引前缀超过最大列数据类型大小,
CREATE TABLE
将处理索引如下:-
对于非唯一索引,如果启用了严格的 SQL 模式,则会发生错误,或者索引长度会减小以符合最大列数据类型大小,并产生警告(如果未启用严格的 SQL 模��)。
-
对于唯一索引,无论 SQL 模式如何,都会发生错误,因为减少索引长度可能会导致插入不符合指定唯一性要求的非唯一条目。
-
-
JSON
列无法被索引。您可以通过在生成的列上创建索引来绕过此限制,该生成的列从JSON
列中提取标量值。详细示例请参见 在生成的列上创建索引以提供 JSON 列索引。
-
-
NOT NULL | NULL
如果未指定
NULL
或NOT NULL
,则该列被视为已指定NULL
。在 MySQL 8.0 中,只有
InnoDB
、MyISAM
和MEMORY
存储引擎支持可以具有NULL
值的列上的索引。在其他情况下,必须将索引列声明为NOT NULL
,否则会产生错误。 -
DEFAULT
为列指定默认值。有关默认值处理的更多信息,包括列定义不包含显式
DEFAULT
值的情况,请参见第 13.6 节,“数据类型默认值”。如果启用了
NO_ZERO_DATE
或NO_ZERO_IN_DATE
SQL 模式,并且日期值默认值不符合该模式,则CREATE TABLE
在严格 SQL 模式未启用时会产生警告,在启用严格模式时会产生错误。例如,启用NO_ZERO_IN_DATE
,c1 DATE DEFAULT '2010-00-00'
会产生警告。 -
VISIBLE
、INVISIBLE
指定列的可见性。如果两个关键字都不存在,则默认为
VISIBLE
。表必须至少有一列可见。尝试使所有列不可见会产生错误。有关更多信息,请参见第 15.1.20.10 节,“不可见列”。VISIBLE
和INVISIBLE
关键字从 MySQL 8.0.23 开始可用。在 MySQL 8.0.23 之前,所有列都是可见的。 -
AUTO_INCREMENT
整数或浮点列可以具有附加属性
AUTO_INCREMENT
。当您将NULL
(推荐)或0
值插入到索引的AUTO_INCREMENT
列中时,该列将设置为下一个序列值。通常这是*
value*+1
,其中value
是当前表中列的最大值。AUTO_INCREMENT
序列从1
开始。在插入行后检索
AUTO_INCREMENT
值,请使用LAST_INSERT_ID()
SQL 函数或mysql_insert_id()
C API 函数。请参见第 14.15 节,“信息函数”,以及 mysql_insert_id()。如果启用了
NO_AUTO_VALUE_ON_ZERO
SQL 模式,则可以将0
存储在AUTO_INCREMENT
列中,而不生成新的序列值。参见第 7.1.11 节,“服务器 SQL 模式”。每个表只能有一个
AUTO_INCREMENT
列,它必须被索引,并且不能有默认值。AUTO_INCREMENT
列只有在包含正值时才能正常工作。插入负数被视为插入一个非常大的正数。这样做是为了避免当数字从正数“环绕”到负数时出现精度问题,并确保您不会意外地获得一个包含0
的AUTO_INCREMENT
列。对于
MyISAM
表,您可以在多列键中指定一个AUTO_INCREMENT
次要列。参见 Section 5.6.9, “Using AUTO_INCREMENT”。要使 MySQL 与某些 ODBC 应用程序兼容,您可以使用以下查询找到最后插入行的
AUTO_INCREMENT
值:SELECT * FROM *tbl_name* WHERE *auto_col* IS NULL
此方法要求
sql_auto_is_null
变量未设置为 0。请参见 Section 7.1.8, “Server System Variables”。有关
InnoDB
和AUTO_INCREMENT
的信息,请参见 Section 17.6.1.6, “AUTO_INCREMENT Handling in InnoDB”。有关AUTO_INCREMENT
和 MySQL 复制的信息,请参见 Section 19.5.1.1, “Replication and AUTO_INCREMENT”。 -
COMMENT
可以使用
COMMENT
选项为列指定长达 1024 个字符的注释。该注释将显示在SHOW CREATE TABLE
和SHOW FULL COLUMNS
语句中。它还显示在信息模式COLUMNS
表的COLUMN_COMMENT
列中。 -
COLUMN_FORMAT
在 NDB Cluster 中,还可以使用
COLUMN_FORMAT
为NDB
表的各个列指定数据存储格式。可接受的列格式包括FIXED
、DYNAMIC
和DEFAULT
。FIXED
用于指定固定宽度存储,DYNAMIC
允许列为可变宽度,DEFAULT
使列使用由列的数据类型确定的固定宽度或可变宽度存储(可能被ROW_FORMAT
修饰符覆盖)。对于
NDB
表,COLUMN_FORMAT
的默认值为FIXED
。在 NDB Cluster 中,使用
COLUMN_FORMAT=FIXED
定义的列的最大可能偏移量为 8188 字节。有关更多信息和可能的解决方法,请参见 Section 25.2.7.5, “Limits Associated with Database Objects in NDB Cluster”。COLUMN_FORMAT
目前对使用除NDB
之外的存储引擎的表的列没有影响。MySQL 8.0 会默默忽略COLUMN_FORMAT
。 -
ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
选项(自 MySQL 8.0.21 起可用)用于指定主存储引擎和辅助存储引擎的列属性。这些选项保留供将来使用。允许的值是包含有效
JSON
文档或空字符串('')的字符串文字。无效的JSON
将被拒绝。CREATE TABLE t1 (c1 INT ENGINE_ATTRIBUTE='{"*key*":"*value*"}');
ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
的值可以重复使用而不会出错。在这种情况下,将使用最后指定的值。ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
的值不会被服务器检查,也不会在表的存储引擎更改时被清除。 -
STORAGE
对于
NDB
表,可以通过使用STORAGE
子句来指定列是存储在磁盘上还是内存中。STORAGE DISK
导致列存储在磁盘上,而STORAGE MEMORY
导致使用内存存储。仍然必须在使用的CREATE TABLE
语句中包含TABLESPACE
子句:mysql> CREATE TABLE t1 ( -> c1 INT STORAGE DISK, -> c2 INT STORAGE MEMORY -> ) ENGINE NDB; ERROR 1005 (HY000): Can't create table 'c.t1' (errno: 140) mysql> CREATE TABLE t1 ( -> c1 INT STORAGE DISK, -> c2 INT STORAGE MEMORY -> ) TABLESPACE ts_1 ENGINE NDB; Query OK, 0 rows affected (1.06 sec)
对于
NDB
表,STORAGE DEFAULT
等同于STORAGE MEMORY
。STORAGE
子句对使用除NDB
之外的存储引擎的表没有影响。STORAGE
关键字仅在随 NDB Cluster 一起提供的 mysqld 构建中受支持;在 MySQL 的任何其他版本中都不被识别,任何尝试使用STORAGE
关键字都会导致语法错误。 -
GENERATED ALWAYS
用于指定生成列表达式。有关生成列的信息,请参阅第 15.1.20.8 节,“CREATE TABLE 和 Generated Columns”。
存储生成列 可以被索引。
InnoDB
支持对虚拟生成列进行二级索引。请参阅第 15.1.20.9 节,“Secondary Indexes and Generated Columns”。
索引、外键和检查约束
创建索引、外键和CHECK
约束时适用几个关键字。除了以下描述外,有关一般背景,请参阅 Section 15.1.15, “CREATE INDEX Statement”,Section 15.1.20.5, “FOREIGN KEY Constraints”和 Section 15.1.20.6, “CHECK Constraints”。
-
CONSTRAINT *
symbol*
可以使用
CONSTRAINT *
symbol*
子句来命名约束。如果未给出该子句,或者在CONSTRAINT
关键字后未包含symbol
,MySQL 会自动生成约束名称,以下列出的情况除外。如果使用了symbol
值,必须对每个模式(数据库)的每种约束类型保持唯一性。重复的symbol
会导致错误。另请参阅有关生成约束标识符长度限制的讨论,详见 Section 11.2.1, “Identifier Length Limits”。注意
如果在外键定义中未给出
CONSTRAINT *
symbol*
子句,或者在CONSTRAINT
关键字后未包含symbol
,MySQL 在 MySQL 8.0.15 之前使用外键索引名称,并在此后自动生成约束名称。SQL 标准规定所有类型的约束(主键、唯一索引、外键、检查)属于同一命名空间。在 MySQL 中,每种约束类型在每个模式中都有自己的命名空间。因此,每种约束类型的名称必须在每个模式中保持唯一,但不同类型的约束可以具有相同的名称。
-
PRIMARY KEY
唯一索引,所有关键列必须定义为
NOT NULL
。如果它们没有明确声明为NOT NULL
,MySQL 会隐式(并悄无声息地)声明它们。一个表只能有一个PRIMARY KEY
。PRIMARY KEY
的名称始终为PRIMARY
,因此不能用作任何其他类型索引的名称。如果没有
PRIMARY KEY
,应用程序要求表中的PRIMARY KEY
,MySQL 将第一个没有NULL
列的UNIQUE
索引作为PRIMARY KEY
返回。在
InnoDB
表中,保持PRIMARY KEY
简短,以减少次要索引的存储开销。每个次要索引条目都包含相应行的主键列的副本。(参见 Section 17.6.2.1, “Clustered and Secondary Indexes”。)在创建的表中,首先放置
PRIMARY KEY
,然后是所有UNIQUE
索引,然后是非唯一索引。这有助于 MySQL 优化器优先考虑使用哪个索引,并更快地检测重复的UNIQUE
键。PRIMARY KEY
可以是多列索引。但是,您不能在列规范中使用PRIMARY KEY
关键属性创建多列索引。这样做只会将该单列标记为主键。您必须使用单独的PRIMARY KEY(*
key_part*, ...)
子句。如果表具有由整数类型组成的单列
PRIMARY KEY
或UNIQUE NOT NULL
索引,您可以在SELECT
语句中使用_rowid
来引用索引列,如 Unique Indexes 中所述。在 MySQL 中,
PRIMARY KEY
的名称是PRIMARY
。对于其他索引,如果您没有分配名称,则该索引将被分配与第一个索引列相同的名称,并带有可选后缀(_2
,_3
,...
)以使其唯一。您可以使用SHOW INDEX FROM *
tbl_name*
查看表的索引名称。参见 Section 15.7.7.22, “SHOW INDEX Statement”。 -
KEY | INDEX
KEY
通常是INDEX
的同义词。在列定义中给出时,PRIMARY KEY
关键属性也可以简单地指定为KEY
。这是为了与其他数据库系统兼容而实现的。 -
UNIQUE
UNIQUE
索引创建一个约束,使索引中的所有值必须是不同的。如果尝试添加具有与现有行匹配的键值的新行,则会发生错误。对于所有引擎,UNIQUE
索引允许对可以包含NULL
的列进行多个NULL
值。如果为UNIQUE
索引的列指定前缀值,则列值必须在前缀长度内是唯一的。如果表具有由整数类型组成的单列
PRIMARY KEY
或UNIQUE NOT NULL
索引,您可以在SELECT
语句中使用_rowid
来引用索引列,如 Unique Indexes 中所述。 -
FULLTEXT
FULLTEXT
索引是用于全文搜索的特殊类型的索引。只有InnoDB
和MyISAM
存储引擎支持FULLTEXT
索引。它们只能从CHAR
、VARCHAR
和TEXT
列创建。索引始终在整个列上进行;不支持列前缀索引,如果指定了任何前缀长度,则会被忽略。有关操作的详细信息,请参见第 14.9 节,“全文搜索函数”。可以指定WITH PARSER
子句作为index_option
值,以将解析器插件与索引关联,如果全文索引和搜索操作需要特殊处理。此子句仅适用于FULLTEXT
索引。InnoDB
和MyISAM
支持全文解析器插件。有关更多信息,请参见全文解析器插件和编写全文解析器插件。 -
空间
您可以在空间数据类型上创建
SPATIAL
索引。空间类型仅支持InnoDB
和MyISAM
表,并且索引列必须声明为NOT NULL
。请参见第 13.4 节,“空间数据类型”。 -
外键
MySQL 支持外键,允许您在表之间交叉引用相关数据,并支持外键约束,有助于保持这些分散数据的一致性。有关定义和选项信息,请参见
reference_definition
,以及reference_option
。使用
InnoDB
存储引擎的分区表不支持外键。有关更多信息,请参见第 26.6 节,“分区的限制和限制”。 -
CHECK
CHECK
子句使得可以创建用于检查表行中数据值的约束。请参见第 15.1.20.6 节,“CHECK 约束”。 -
*
key_part*
-
key_part
规范可以以ASC
或DESC
结尾,以指定索引值是按升序还是降序存储。如果没有给出顺序说明符,则默认为升序。 -
前缀,由
length
属性定义,对于使用REDUNDANT
或COMPACT
行格式的InnoDB
表最多可以达到 767 字节长。对于使用DYNAMIC
或COMPRESSED
行格式的InnoDB
表,前缀长度限制为 3072 字节。对于MyISAM
表,前缀长度限制为 1000 字节。前缀限制以字节为单位。但是,在
CREATE TABLE
、ALTER TABLE
和CREATE INDEX
语句中的索引规范中,对于非二进制字符串类型(CHAR
、VARCHAR
、TEXT
),前缀长度被解释为字符数,对于二进制字符串类型(BINARY
、VARBINARY
、BLOB
),前缀长度被解释为字节数。在为使用多字节字符集的非二进制字符串列指定前缀长度时,请考虑这一点。 -
从 MySQL 8.0.17 开始,
key_part
规范的expr
可以采用形式(CAST *
json_path* AS *
type* ARRAY)
,以在JSON
列上创建多值索引。多值索引提供了有关创建、使用以及多值索引的限制和限制的详细信息。
-
-
*
index_type*
一些存储引擎允许在创建索引时指定索引类型。
index_type
指定符的语法是USING *
type_name*
。例子:
CREATE TABLE lookup (id INT, INDEX USING BTREE (id)) ENGINE = MEMORY;
USING
的首选位置是在索引列列表之后。它可以在列列表之前给出,但是在该位置使用该选项的支持已被弃用,您应该期望在未来的 MySQL 版本中将其移除。 -
*
index_option*
index_option
值指定索引的附加选项。-
KEY_BLOCK_SIZE
对于
MyISAM
表,KEY_BLOCK_SIZE
可选地指定用于索引键块的字节大小。该值被视为提示;如果需要,可以使用不同的大小。为单个索引定义指定的KEY_BLOCK_SIZE
值会覆盖表级别的KEY_BLOCK_SIZE
值。有关表级别
KEY_BLOCK_SIZE
属性的信息,请参阅表选项。 -
WITH PARSER
WITH PARSER
选项只能与FULLTEXT
索引一起使用。如果全文索引和搜索操作需要特殊处理,则将解析器插件与索引关联起来。InnoDB
和MyISAM
支持全文解析器插件。如果您有一个带有关联全文解析器插件的MyISAM
表,您可以使用ALTER TABLE
将表转换为InnoDB
。 -
COMMENT
索引定义可以包括最多 1024 个字符的可选注释。
你可以使用
*
index_option*
COMMENT
子句为单个索引设置InnoDB
MERGE_THRESHOLD
值。参见第 17.8.11 节,“配置索引页合并阈值”。 -
VISIBLE
,INVISIBLE
指定索引可见性。索引默认可见。不可见索引不会被优化器使用。索引可见性的规范适用于主键之外的索引(显式或隐式)。更多信息,请参见第 10.3.12 节,“不可见索引”。
-
ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
选项(自 MySQL 8.0.21 起可用)用于指定主要和次要存储引擎的索引属性。这些选项保留供将来使用。
有关可接受的
index_option
值的更多信息,请参见第 15.1.15 节,“CREATE INDEX 语句”。有关索引的更多信息,请参见第 10.3.1 节,“MySQL 如何使用索引”。 -
-
*
reference_definition*
有关
reference_definition
语法详细信息和示例,请参见第 15.1.20.5 节,“外键约束”。InnoDB
和NDB
表支持检查外键约束。引用表的列必须始终明确命名。外键支持ON DELETE
和ON UPDATE
操作。有关更详细的信息和示例,请参见第 15.1.20.5 节,“外键约束”。对于其他存储引擎,MySQL 服务器会解析并忽略
CREATE TABLE
语句中的FOREIGN KEY
语法。重要
对于熟悉 ANSI/ISO SQL 标准的用户,请注意,包括
InnoDB
在内的任何存储引擎都不识别或强制执行用于参照完整性约束定义的MATCH
子句。明确指定MATCH
子句不会产生指定的效果,并且还会导致ON DELETE
和ON UPDATE
子句被忽略。因此,应避免指定MATCH
。SQL 标准中的
MATCH
子句控制如何处理复合(多列)外键中的NULL
值与主键进行比较。InnoDB
基本上实现了MATCH SIMPLE
定义的语义,允许外键全部或部分为NULL
。在这种情况下,包含这种外键的(子表)行允许被插入,并且不匹配参考(父表)中的任何行。可以使用触发器实现其他语义。此外,MySQL 要求被引用的列需要建立索引以提高性能。然而,
InnoDB
不强制要求被引用的列声明为UNIQUE
或NOT NULL
。对于引用非唯一键或包含NULL
值的键的外键引用的处理在UPDATE
或DELETE CASCADE
等操作中没有明确定义。建议使用只引用既UNIQUE
(或PRIMARY
)又NOT NULL
键的外键。MySQL 解析但忽略“内联
REFERENCES
规范”(如 SQL 标准中定义的)中的引用,其中引用被定义为列规范的一部分。只有在作为单独的FOREIGN KEY
规范的一部分指定时,MySQL 才接受REFERENCES
子句。有关更多信息,请参见 Section 1.6.2.3, “FOREIGN KEY Constraint Differences”。 -
*
reference_option*
有关
RESTRICT
、CASCADE
、SET NULL
、NO ACTION
和SET DEFAULT
选项的信息,请参见 Section 15.1.20.5, “FOREIGN KEY Constraints”。
表选项
表选项用于优化表的行为。在大多数情况下,您不必指定任何选项。除非另有说明,这些选项适用于所有存储引擎。不适用于给定存储引擎的选项可能会被接受并记入表定义中。如果以后使用ALTER TABLE
将表转换为使用不同的存储引擎,则这些选项将适用。
-
ENGINE
指定表的存储引擎,可以使用以下表中显示的名称之一。引擎名称可以是带引号或不带引号的。带引号的名称
'DEFAULT'
会被识别但会被忽略。存储引擎 描述 InnoDB
具有行锁定和外键的事务安全表。新表的默认存储引擎。参见第十七章,InnoDB 存储引擎,特别是如果您有 MySQL 经验但是对 InnoDB
不熟悉,请参见第 17.1 节,“InnoDB 简介”。MyISAM
主要用于只读或读取频繁工作负载的二进制便携式存储引擎。参见第 18.2 节,“MyISAM 存储引擎”。 MEMORY
这种存储引擎的数据仅存储在内存中。参见第 18.3 节,“MEMORY 存储引擎”。 CSV
以逗号分隔值格式存储行的表。参见第 18.4 节,“CSV 存储引擎”。 ARCHIVE
存档存储引擎。参见第 18.5 节,“ARCHIVE 存储引擎”。 EXAMPLE
一个示例引擎。参见第 18.9 节,“EXAMPLE 存储引擎”。 FEDERATED
访问远程表的存储引擎。参见第 18.8 节,“FEDERATED 存储引擎”。 HEAP
这是 MEMORY
的同义词。MERGE
一组作为一个表使用的 MyISAM
表。也称为MRG_MyISAM
。参见第 18.7 节,“MERGE 存储引擎”。NDB
集群化、容错、基于内存的表,支持事务和外键。也称为 NDBCLUSTER
。参见第二十五章,MySQL NDB Cluster 8.0。存储引擎 描述 默认情况下,如果指定了不可用的存储引擎,语句将失败并显示错误。您可以通过从服务器 SQL 模式中删除
NO_ENGINE_SUBSTITUTION
来覆盖此行为,以便 MySQL 允许将指定的引擎替换为默认存储引擎。通常在这种情况下,默认值为default_storage_engine
系统变量的InnoDB
。当禁用NO_ENGINE_SUBSTITUTION
时,如果未遵守存储引擎规范,则会发出警告。 -
AUTOEXTEND_SIZE
定义
InnoDB
在表空间满时扩展的量。在 MySQL 8.0.23 中引入。设置必须是 4MB 的倍数。默认设置为 0,这将导致表空间根据隐式默认行为进行扩展。有关更多信息,请参见第 17.6.3.9 节,“表空间 AUTOEXTEND_SIZE 配置”。 -
AUTO_INCREMENT
表的初始
AUTO_INCREMENT
值。在 MySQL 8.0 中,这适用于MyISAM
、MEMORY
、InnoDB
和ARCHIVE
表。对于不支持AUTO_INCREMENT
表选项的引擎,要设置第一个自动增量值,需要在创建表后插入一个值比所需值小 1 的“虚拟”行,然后删除虚拟行。对于支持在
CREATE TABLE
语句中使用AUTO_INCREMENT
表选项的引擎,您也可以使用ALTER TABLE *
tbl_name* AUTO_INCREMENT = *
N*
来重置AUTO_INCREMENT
值。该值不能低于当前列中的最大值。 -
AVG_ROW_LENGTH
表的平均行长度的近似值。只需为具有可变大小行的大表设置此值。
当您创建一个
MyISAM
表时,MySQL 使用MAX_ROWS
和AVG_ROW_LENGTH
选项的乘积来决定生成的表有多大。如果您没有指定任何选项,MyISAM
数据和索引文件的最大大小默认为 256TB。(如果您的操作系统不支持那么大的文件,表大小将受文件大小限制。)如果您想要减小指针大小以使索引更小更快,并且您实际上不需要大文件,您可以通过设置myisam_data_pointer_size
系统变量来减小默认指针大小。(参见第 7.1.8 节,“服务器系统变量”。)如果您希望所有表都能超过默认限制增长,并且愿意让表比必要的稍慢和稍大,您可以通过设置此变量来增加默认指针大小。将值设置为 7 允许表大小达到 65,536TB。 -
[DEFAULT] 字符集
为表指定默认字符集。
CHARSET
是CHARACTER SET
的同义词。如果字符集名称为DEFAULT
,则使用数据库字符集。 -
校验和
如果您希望 MySQL 为所有行维护一个实时校验和(即 MySQL 在表更改时自动更新的校验和),请将此设置为 1。这使得表更新稍慢,但也更容易找到损坏的表。
CHECKSUM TABLE
语句报告校验和。(仅适用于MyISAM
。) -
[DEFAULT] 校对规则
为表指定默认排序规则。
-
注释
表的注释,最长可达 2048 个字符。
您可以使用
*
table_option*
COMMENT
子句为表设置InnoDB
MERGE_THRESHOLD
值。请参阅 Section 17.8.11, “Configuring the Merge Threshold for Index Pages”。设置 NDB_TABLE 选项。 在创建
NDB
表的CREATE TABLE
或修改一个的ALTER TABLE
语句中,表注释也可用于指定NDB_TABLE
选项NOLOGGING
、READ_BACKUP
、PARTITION_BALANCE
或FULLY_REPLICATED
中的一个到四个作为一组名称-值对,如果需要,用逗号分隔,紧随以NDB_TABLE=
开头的引用注释文本。以下是使用此语法的示例语句(强调文本):CREATE TABLE t1 ( c1 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, c2 VARCHAR(100), c3 VARCHAR(100) ) ENGINE=NDB *COMMENT="NDB_TABLE=READ_BACKUP=0,PARTITION_BALANCE=FOR_RP_BY_NODE"*;
引号字符串内不允许有空格。字符串不区分大小写。
该注释将显示在
SHOW CREATE TABLE
的输出中。注释的文本也作为 MySQL 信息模式TABLES
表的 TABLE_COMMENT 列中可用。这种注释语法也适用于
NDB
表的ALTER TABLE
语句。请注意,与ALTER TABLE
一起使用的表注释会替换表先前可能具有的任何现有注释。不支持在表注释中设置
MERGE_THRESHOLD
选项用于NDB
表(将被忽略)。有关完整的语法信息和示例,请参阅 Section 15.1.20.12, “Setting NDB Comment Options”。
-
COMPRESSION
用于
InnoDB
表的页面级压缩的压缩算法。支持的值包括Zlib
、LZ4
和None
。COMPRESSION
属性是通过透明页面压缩功能引入的。页面压缩仅支持驻留在 file-per-table 表空间中的InnoDB
表,并且仅在支持稀疏文件和孔打孔的 Linux 和 Windows 平台上可用。有关更多信息,请参阅 Section 17.9.2, “InnoDB Page Compression”。 -
CONNECTION
FEDERATED
表的连接字符串。注意
旧版本的 MySQL 使用
COMMENT
选项作为连接字符串的注释。 -
DATA DIRECTORY
、INDEX DIRECTORY
对于
InnoDB
,DATA DIRECTORY='*
目录*'
子句允许在数据目录之外创建表。必须启用innodb_file_per_table
变量才能使用DATA DIRECTORY
子句。必须指定完整的目录路径。截至 MySQL 8.0.21,指定的目录必须为InnoDB
所知。有关更多信息,请参阅第 17.6.1.2 节,“外部创建表”。在创建
MyISAM
表时,可以使用DATA DIRECTORY='*
目录*'
子句,INDEX DIRECTORY='*
目录*'
子句,或两者。它们分别指定MyISAM
表的数据文件和索引文件放置的位置。与InnoDB
表不同,当使用DATA DIRECTORY
或INDEX DIRECTORY
选项创建MyISAM
表时,MySQL 不会创建与数据库名称对应的子目录。文件将在指定的目录中创建。您必须具有
FILE
权限才能使用DATA DIRECTORY
或INDEX DIRECTORY
表选项。重要
对于分区表,将忽略表级
DATA DIRECTORY
和INDEX DIRECTORY
选项。(Bug #32091)仅当您未使用
--skip-symbolic-links
选项时,这些选项才有效。您的操作系统还必须具有可用的、线程安全的realpath()
调用。有关更完整的信息,请参阅第 10.12.2.2 节,“在 Unix 上为 MyISAM 表使用符号链接”。如果使用没有
DATA DIRECTORY
选项创建MyISAM
表,则.MYD
文件将在数据库目录中创建。默认情况下,如果MyISAM
在这种情况下找到现有的.MYD
文件,则会覆盖它。对于使用没有INDEX DIRECTORY
选项创建的表的.MYI
文件也适用相同规则。要抑制此行为,请使用--keep_files_on_create
选项启动服务器,在这种情况下,MyISAM
不会覆盖现有文件,而是返回错误。如果使用
DATA DIRECTORY
或INDEX DIRECTORY
选项创建MyISAM
表,并且找到现有的.MYD
或.MYI
文件,MyISAM
将始终返回错误,并且不会覆盖指定目录中的文件。重要
您不能使用包含 MySQL 数据目录的路径名与
DATA DIRECTORY
或INDEX DIRECTORY
一起使用。这包括分区表和单个表分区。(参见 Bug #32167。) -
DELAY_KEY_WRITE
如果您希望延迟表的键更新直到表关闭,请将此设置为 1。请参阅第 7.1.8 节,“服务器系统变量”中的
delay_key_write
系统变量的描述。(仅适用于MyISAM
。) -
ENCRYPTION
ENCRYPTION
子句用于启用或禁用InnoDB
表的页面级数据加密。必须在启用加密之前安装和配置密钥环插件。在 MySQL 8.0.16 之前,只能在每个表的表空间中创建表时指定ENCRYPTION
子句。从 MySQL 8.0.16 开始,也可以在通用表空间中创建表时指定ENCRYPTION
子句。截至 MySQL 8.0.16,如果未指定
ENCRYPTION
子句,表将继承默认的模式加密。如果启用了table_encryption_privilege_check
变量,则需要TABLE_ENCRYPTION_ADMIN
权限才能创建具有与默认模式加密不同的ENCRYPTION
子句设置的表。在通用表空间中创建表时,表和表空间加密必须匹配。从 MySQL 8.0.16 开始,在使用不支持加密的存储引擎时,不允许指定值为
'N'
或''
以外的ENCRYPTION
子句。以前,该子句被接受。有关更多信息,请参见第 17.13 节,“InnoDB 数据静态加密”。
-
ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
选项(自 MySQL 8.0.21 起可用)用于指定主存储引擎和次要存储引擎的表属性。这些选项保留供将来使用。允许的值是包含有效
JSON
文档的字符串文字或空字符串('')。无效的JSON
会被拒绝。CREATE TABLE t1 (c1 INT) ENGINE_ATTRIBUTE='{"*key*":"*value*"}';
ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
值可以重复而不会出错。在这种情况下,将使用最后指定的值。服务器不会检查
ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
值,也不会在更改表的存储引擎时清除这些值。 -
INSERT_METHOD
如果要向
MERGE
表插入数据,必须使用INSERT_METHOD
指定要将行插入的表。INSERT_METHOD
仅适用于MERGE
表的选项。使用FIRST
或LAST
的值将插入到第一个或最后一个表中,使用NO
的值将阻止插入。请参见第 18.7 节,“MERGE 存储引擎”。 -
KEY_BLOCK_SIZE
对于
MyISAM
表,KEY_BLOCK_SIZE
可选地指定用于索引键块的字节大小。该值被视为提示;如果需要,可以使用不同的大小。为单个索引定义指定的KEY_BLOCK_SIZE
值会覆盖表级别的KEY_BLOCK_SIZE
值。对于
InnoDB
表,KEY_BLOCK_SIZE
指定了用于压缩InnoDB
表的页大小(以千字节为单位)。KEY_BLOCK_SIZE
值被视为提示;如果必要,InnoDB
可能会使用不同的大小。KEY_BLOCK_SIZE
只能小于或等于innodb_page_size
值。值为 0 表示默认的压缩页大小,即innodb_page_size
值的一半。根据innodb_page_size
,可能的KEY_BLOCK_SIZE
值包括 0、1、2、4、8 和 16。更多信息请参见 Section 17.9.1, “InnoDB Table Compression”。Oracle 建议在为
InnoDB
表指定KEY_BLOCK_SIZE
时启用innodb_strict_mode
。当启用innodb_strict_mode
时,指定无效的KEY_BLOCK_SIZE
值会返回错误。如果禁用innodb_strict_mode
,无效的KEY_BLOCK_SIZE
值会导致警告,并且KEY_BLOCK_SIZE
选项会被忽略。对于
SHOW TABLE STATUS
响应中的Create_options
列报告表实际使用的KEY_BLOCK_SIZE
,SHOW CREATE TABLE
也是如此。InnoDB
仅支持表级别的KEY_BLOCK_SIZE
。KEY_BLOCK_SIZE
不支持 32KB 和 64KB 的innodb_page_size
值。InnoDB
表压缩不支持这些页大小。在创建临时表时,
InnoDB
不支持KEY_BLOCK_SIZE
选项。 -
MAX_ROWS
您计划存储在表中的最大行数。这不是一个硬限制,而是一个提示,告诉存储引擎表必须至少能够存储这么多行。
重要
使用
MAX_ROWS
来控制表分区数量的NDB
表已被弃用。为了向后兼容,它在后续版本中仍然受支持,但可能在未来的版本中被移除。请改用 PARTITION_BALANCE;参见设置 NDB_TABLE 选项。NDB
存储引擎将此值视为最大值。如果计划创建非常大的 NDB Cluster 表(包含数百万行),应使用此选项确保NDB
为存储表主键哈希的哈希表分配足够数量的索引槽,方法是设置MAX_ROWS = 2 * *
rows*
,其中rows
是您预计插入表中的行数。最大的
MAX_ROWS
值为 4294967295;更大的值将被截断为此限制。 -
MIN_ROWS
您计划在表中存储的最小行数。
MEMORY
存储引擎将此选项用作有关内存使用的提示。 -
PACK_KEYS
仅对
MyISAM
表生效。如果想要更小的索引,请将此选项设置为 1。通常这会使更新变慢,读取变快。将选项设置为 0 会禁用所有键的压缩。将其设置为DEFAULT
会告诉存储引擎只压缩长的CHAR
、VARCHAR
、BINARY
或VARBINARY
列。如果不使用
PACK_KEYS
,默认情况下会压缩字符串,但不会压缩数字。如果使用PACK_KEYS=1
,数字也会被压缩。在压缩二进制数字键时,MySQL 使用前缀压缩:
-
每个键需要额外一个字节来指示前一个键的多少个字节与下一个键相同。
-
指向行的指针以高字节优先顺序直接存储在键后面,以提高压缩效果。
这意味着如果在两个连续行上有许多相同的键,通常后续的“相同”键只需要两个字节(包括指向行的指针)。与普通情况下后续键需要
storage_size_for_key + pointer_size
(其中指针大小通常为 4)相比,如果有许多相同的数字,则通过前缀压缩可以获得显著的好处。如果所有键都完全不同,每个键会多使用一个字节,如果键不是可以有NULL
值的键。在这种情况下,压缩的键长度存储在用于标记键是否为NULL
的同一个字节中。 -
-
PASSWORD
此选项未使用。
-
ROW_FORMAT
定义了行存储的物理格式。
在禁用严格模式时创建表时,如果指定的行格式不受支持,则使用存储引擎的默认行格式。表的实际行格式将在响应
SHOW TABLE STATUS
时的Row_format
列中报告。Create_options
列显示了在CREATE TABLE
语句中指定的行格式,SHOW CREATE TABLE
也是如此。表使用的行格式取决于用于表的存储引擎。
对于
InnoDB
表:-
默认行格式由
innodb_default_row_format
定义,其默认设置为DYNAMIC
。当未定义ROW_FORMAT
选项或使用ROW_FORMAT=DEFAULT
时,将使用默认行格式。如果未定义
ROW_FORMAT
选项,或者使用ROW_FORMAT=DEFAULT
,重建表的操作也会将表的行格式悄悄地更改为由innodb_default_row_format
定义的默认值。有关更多信息,请参见定义表的行格式。 -
为了更有效地存储数据类型,特别是
BLOB
类型,使用DYNAMIC
。有关与DYNAMIC
行格式相关的要求,请参见 DYNAMIC 行格式。 -
要为
InnoDB
表启用压缩,请指定ROW_FORMAT=COMPRESSED
。在创建临时表时,不支持ROW_FORMAT=COMPRESSED
选项。有关与COMPRESSED
行格式相关的要求,请参见第 17.9 节,“InnoDB 表和页面压缩”。 -
旧版本的 MySQL 中使用的行格式仍可通过指定
REDUNDANT
行格式来请求。 -
当指定非默认的
ROW_FORMAT
子句时,考虑同时启用innodb_strict_mode
配置选项。 -
不支持
ROW_FORMAT=FIXED
。如果在禁用innodb_strict_mode
的情况下指定了ROW_FORMAT=FIXED
,InnoDB
会发出警告并假定ROW_FORMAT=DYNAMIC
。如果在启用innodb_strict_mode
(默认情况下)的情况下指定了ROW_FORMAT=FIXED
,InnoDB
会返回错误。 -
有关
InnoDB
行格式的更多信息,请参见第 17.10 节,“InnoDB 行格式”。
对于
MyISAM
表,选项值可以是FIXED
或DYNAMIC
,用于静态或变长行格式。myisampack将类型设置为COMPRESSED
。请参见 Section 18.2.3, “MyISAM Table Storage Formats”。对于
NDB
表,默认的ROW_FORMAT
是DYNAMIC
。 -
-
START TRANSACTION
这是一个内部使用的表选项。它在 MySQL 8.0.21 中引入,允许将
CREATE TABLE ... SELECT
作为单个原子事务记录在二进制日志中,当使用支持原子 DDL 的存储引擎进行基于行的复制时。在CREATE TABLE ... START TRANSACTION
之后只允许BINLOG
、COMMIT
和ROLLBACK
语句。有关相关信息,请参见 Section 15.1.1, “Atomic Data Definition Statement Support”。 -
STATS_AUTO_RECALC
指定是否自动重新计算
InnoDB
表的持久性统计信息。值DEFAULT
会导致表的持久性统计设置由innodb_stats_auto_recalc
配置选项确定。值1
会导致当表中的数据变化了 10%时重新计算统计信息。值0
会阻止对该表进行自动重新计算;在这种设置下,在对表进行重大更改后,发出ANALYZE TABLE
语句重新计算统计信息。有关持久性统计功能的更多信息,请参见 Section 17.8.10.1, “Configuring Persistent Optimizer Statistics Parameters”。 -
STATS_PERSISTENT
指定是否为
InnoDB
表启用持久性统计信息。值DEFAULT
导致表的持久性统计信息设置由innodb_stats_persistent
配置选项确定。值1
为表启用持久性统计信息,而值0
关闭此功能。通过CREATE TABLE
或ALTER TABLE
语句启用持久性统计信息后,需发出ANALYZE TABLE
语句来计算统计信息,在将代表性数据加载到表中后。有关持久性统计信息功能的更多信息,请参见第 17.8.10.1 节,“配置持久性优化器统计参数”。 -
STATS_SAMPLE_PAGES
在估算索引列的基数和其他统计信息时要采样的索引页数,例如由
ANALYZE TABLE
计算的那些。有关更多信息,请参见第 17.8.10.1 节,“配置持久性优化器统计参数”。 -
TABLESPACE
TABLESPACE
子句可用于在现有的通用表空间、每个表的文件表空间或系统表空间中创建InnoDB
表。CREATE TABLE *tbl_name* ... TABLESPACE [=] *tablespace_name*
您指定的通用表空间必须在使用
TABLESPACE
子句之前存在。有关通用表空间的信息,请参见第 17.6.3.3 节,“通用表空间”。*
tablespace_name*
是区分大小写的标识符。它可以带引号也可以不带。不允许使用斜杠字符(“/”)。以“innodb_”开头的名称保留供特殊用途。要在系统表空间中创建表,请将
innodb_system
指定为表空间名称。CREATE TABLE *tbl_name* ... TABLESPACE [=] innodb_system
使用
TABLESPACE [=] innodb_system
,您可以将任何未压缩行格式的表放置在系统表空间中,而不受innodb_file_per_table
设置的影响。例如,您可以使用TABLESPACE [=] innodb_system
将具有ROW_FORMAT=DYNAMIC
的表添加到系统表空间中。要在每个表的文件表空间中创建表,请将
innodb_file_per_table
指定为表空间名称。CREATE TABLE *tbl_name* ... TABLESPACE [=] innodb_file_per_table
注意
如果启用了
innodb_file_per_table
,则无需指定TABLESPACE=innodb_file_per_table
来创建InnoDB
每个表的文件表空间。当启用innodb_file_per_table
时,默认情况下在每个表的文件表空间中创建InnoDB
表。DATA DIRECTORY
子句允许与CREATE TABLE ... TABLESPACE=innodb_file_per_table
一起使用,但在与TABLESPACE
子句结合使用时不受支持。从 MySQL 8.0.21 开始,DATA DIRECTORY
子句中指定的目录必须为InnoDB
所知。更多信息,请参阅使用 DATA DIRECTORY 子句。注意
从 MySQL 8.0.13 开始,使用
TABLESPACE = innodb_file_per_table
和TABLESPACE = innodb_temporary
子句与CREATE TEMPORARY TABLE
已不再受支持;预计将在将来的 MySQL 版本中删除。STORAGE
表选项仅用于NDB
表。STORAGE
确定所使用的存储类型,可以是DISK
或MEMORY
中的任一种。TABLESPACE ... STORAGE DISK
将表分配给 NDB Cluster Disk Data 表空间。除非在TABLESPACE
tablespace_name
之前,否则不能在CREATE TABLE
中使用STORAGE DISK
。对于
STORAGE MEMORY
,表空间名称是可选的,因此,您可以使用TABLESPACE *
tablespace_name* STORAGE MEMORY
或简单地使用STORAGE MEMORY
来明确指定表位于内存中。有关更多信息,请参阅第 25.6.11 节,“NDB Cluster Disk Data Tables”。
-
UNION
用于将一组相同的
MyISAM
表作为一个表访问。这仅适用于MERGE
表。请参阅第 18.7 节,“MERGE 存储引擎”。您必须对映射到
MERGE
表的表具有SELECT
、UPDATE
和DELETE
权限。注意
以前,所有使用的表都必须与
MERGE
表本身位于同一个数据库中。这个限制不再适用。
表分区
partition_options
可用于控制使用CREATE TABLE
创建的表的分区。
此部分开头显示的partition_options
语法中并非所有选项适用于所有分区类型。有关每种类型的特定信息,请参阅以下各个类型的列表,并参阅第二十六章,分区,以获取有关 MySQL 中分区工作原理和用途的更完整信息,以及有关表创建和其他与 MySQL 分区相关的语句的其他示例。
分区可以被修改、合并、添加到表中,也可以从表中删除。有关完成这些任务的 MySQL 语句的基本信息,请参见 Section 15.1.9, “ALTER TABLE Statement”。有关更详细的描述和示例,请参见 Section 26.3, “Partition Management”。
-
PARTITION BY
如果使用了
partition_options
子句,它以PARTITION BY
开头。这个子句包含用于确定分区的函数;该函数返回一个从 1 到num
的整数值,其中num
是分区的数量。(一个表中可以包含的用户定义分区的最大数量是 1024;本节稍后讨论的子分区数量也包括在这个最大值中。)注意
在
PARTITION BY
子句中使用的表达式 (expr
) 不能引用不在被创建的表中的任何列;这样的引用是明确不允许的,并会导致语句失败并出现错误。 (Bug #29444) -
HASH(*
expr*)
对一个或多个列进行哈希运算,创建一个用于定位和查找行的键。
expr
是使用一个或多个表列的表达式。这可以是任何有效的 MySQL 表达式(包括 MySQL 函数),产生一个单个整数值。例如,以下是使用PARTITION BY HASH
的两个有效的CREATE TABLE
语句:CREATE TABLE t1 (col1 INT, col2 CHAR(5)) PARTITION BY HASH(col1); CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATETIME) PARTITION BY HASH ( YEAR(col3) );
不能在
PARTITION BY HASH
中使用VALUES LESS THAN
或VALUES IN
子句。PARTITION BY HASH
使用expr
除以分区数后的余数(即模数)。有关示例和额外信息,请参见 Section 26.2.4, “HASH Partitioning”。LINEAR
关键字涉及一个略有不同的算法。在这种情况下,存储行的分区号是通过一个或多个逻辑AND
操作的结果计算得出的。有关线性哈希的讨论和示例,请参见 Section 26.2.4.1, “LINEAR HASH Partitioning”。 -
KEY(*
column_list*)
这类似于
HASH
,不同之处在于 MySQL 提供了哈希函数,以保证数据均匀分布。column_list
参数只是一个包含 1 个或多个表列(最多:16)的列表。这个示例展示了一个简单的按键分区的表,有 4 个分区:CREATE TABLE tk (col1 INT, col2 CHAR(5), col3 DATE) PARTITION BY KEY(col3) PARTITIONS 4;
对于按键分区的表,您可以使用
LINEAR
关键字来采用线性分区。这与按HASH
分区的表具有相同的效果。也就是说,分区号是使用&
运算符而不是取模来确定的(详见第 26.2.4.1 节,“线性 HASH 分区”和第 26.2.5 节,“KEY 分区”)。此示例使用按键进行线性分区以在 5 个分区之间分发数据:CREATE TABLE tk (col1 INT, col2 CHAR(5), col3 DATE) PARTITION BY LINEAR KEY(col3) PARTITIONS 5;
ALGORITHM={1 | 2}
选项支持[SUB]PARTITION BY [LINEAR] KEY
。ALGORITHM=1
使服务器使用与 MySQL 5.1 相同的键哈希函数;ALGORITHM=2
表示服务器使用 MySQL 5.5 及更高版本中默认实现和使用的键哈希函数。 (使用 MySQL 5.5 及更高版本中实施的键哈希函数创建的分区表不能被 MySQL 5.1 服务器使用。)不指定该选项与使用ALGORITHM=2
具有相同的效果。该选项主要用于在 MySQL 5.1 和后续 MySQL 版本之间升级或降级[LINEAR] KEY
分区表时,或者在 MySQL 5.5 或更高版本服务器上创建按KEY
或LINEAR KEY
分区的表,该表可以在 MySQL 5.1 服务器上使用。有关更多信息,请参见第 15.1.9.1 节,“ALTER TABLE 分区操作”。mysqldump将此选项写入版本化注释中。
在必要时,
ALGORITHM=1
将显示在使用版本化注释的SHOW CREATE TABLE
输出中,方式与mysqldump相同。即使在创建原始表时指定了此选项,SHOW CREATE TABLE
输出中也总是省略ALGORITHM=2
。您不能在
PARTITION BY KEY
中使用VALUES LESS THAN
或VALUES IN
子句。 -
RANGE(*
表达式*)
在这种情况下,表达式使用一组
VALUES LESS THAN
运算符显示一系列值的范围。在使用范围分区时,您必须至少定义一个使用VALUES LESS THAN
的分区。您不能在范围分区中使用VALUES IN
。注意
对于按
RANGE
分区的表,VALUES LESS THAN
必须与整数文字值或评估为单个整数值的表达式一起使用。在 MySQL 8.0 中,您可以在使用PARTITION BY RANGE COLUMNS
定义的表中克服这一限制,如本节后面所述。假设您有一个希望根据以下方案对包含年份值的列进行分区的表。
分区号: 年份范围: 0 1990 年及之前 1 1991 至 1994 年 2 1995 to 1998 3 1999 to 2002 4 2003 to 2005 5 2006 and later 实现这种分区方案的表可以通过以下
CREATE TABLE
语句实现:CREATE TABLE t1 ( year_col INT, some_data 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 (2002), PARTITION p4 VALUES LESS THAN (2006), PARTITION p5 VALUES LESS THAN MAXVALUE );
PARTITION ... VALUES LESS THAN ...
语句按顺序工作。VALUES LESS THAN MAXVALUE
用于指定大于其他指定最大值的“剩余”值。VALUES LESS THAN
子句按顺序工作,类似于switch ... case
块的case
部分(在许多编程语言中如 C、Java 和 PHP 中找到)。也就是说,这些子句必须按照每个连续VALUES LESS THAN
中指定的上限大于前一个的方式排列,其中引用MAXVALUE
的子句在列表中最后出现。 -
RANGE COLUMNS(*
column_list*)
这种
RANGE
变体便于对使用多列范围条件的查询进行分区修剪(即,具有诸如WHERE a = 1 AND b < 10
或WHERE a = 1 AND b = 10 AND c < 10
条件的查询)。它允许您通过在COLUMNS
子句中列出的列列表和在每个PARTITION ... VALUES LESS THAN (*
value_list*)
分区定义子句中设置的列值集来指定多列中的值范围。(在最简单的情况下,此集合由单个列组成。)在column_list
和value_list
中引用的列的最大数量为 16。在
COLUMNS
子句中使用的column_list
可能只包含列名;列表中的每个列必须是以下 MySQL 数据类型之一:整数类型;字符串类型;时间或日期列类型。不允许使用BLOB
、TEXT
、SET
、ENUM
、BIT
或空间数据类型的列;也不允许使用浮点数类型的列。您也不能在COLUMNS
子句中使用函数或算术表达式。在分区定义中使用的
VALUES LESS THAN
子句必须为COLUMNS()
子句中出现的每个列指定一个文字值;也就是说,用于每个VALUES LESS THAN
子句的值列表必须包含与COLUMNS
子句中列出的列数相同的值。尝试在VALUES LESS THAN
子句中使用比COLUMNS
子句中列出的列数更多或更少的值会导致出现错误 Inconsistency in usage of column lists for partitioning...。您不能对出现在VALUES LESS THAN
中的任何值使用NULL
。可以多次使用MAXVALUE
来表示给定列,如下例所示:CREATE TABLE rc ( a INT NOT NULL, b INT NOT NULL ) PARTITION BY RANGE COLUMNS(a,b) ( PARTITION p0 VALUES LESS THAN (10,5), PARTITION p1 VALUES LESS THAN (20,10), PARTITION p2 VALUES LESS THAN (50,MAXVALUE), PARTITION p3 VALUES LESS THAN (65,MAXVALUE), PARTITION p4 VALUES LESS THAN (MAXVALUE,MAXVALUE) );
在
VALUES LESS THAN
值列表中使用的每个值必须与相应列的类型完全匹配;不会进行任何转换。例如,您不能为与使用整数类型的列匹配的值使用字符串'1'
(您必须使用数字1
),也不能为与使用字符串类型的列匹配的值使用数字1
(在这种情况下,您必须使用带引号的字符串:'1'
)。有关更多信息,请参见第 26.2.1 节,“RANGE 分区”和第 26.4 节,“分区修剪”。
-
LIST(*
expr*)
当基于具有受限可能值集的表列分配分区时,这是很有用的,例如州或国家代码。在这种情况下,所有属于某个州或国家的行可以分配到一个分区,或者可以为某个州或国家集保留一个分区。它类似于
RANGE
,只是每个分区只能使用VALUES IN
来指定可允许的值。VALUES IN
用于匹配的值列表。例如,您可以创建以下分区方案:CREATE TABLE client_firms ( id INT, name VARCHAR(35) ) PARTITION BY LIST (id) ( PARTITION r0 VALUES IN (1, 5, 9, 13, 17, 21), PARTITION r1 VALUES IN (2, 6, 10, 14, 18, 22), PARTITION r2 VALUES IN (3, 7, 11, 15, 19, 23), PARTITION r3 VALUES IN (4, 8, 12, 16, 20, 24) );
在使用列表分区时,您必须至少定义一个使用
VALUES IN
的分区。您不能在PARTITION BY LIST
中使用VALUES LESS THAN
。注意
对于通过
LIST
分区的表,与VALUES IN
一起使用的值列表必须仅包含整数值。在 MySQL 8.0 中,您可以通过后面在本节中描述的LIST COLUMNS
进行分区来克服这种限制。 -
LIST COLUMNS(*
column_list*)
这种
LIST
的变体为使用多列比较条件的查询提供了分区修剪的便利(即,具有诸如WHERE a = 5 AND b = 5
或WHERE a = 1 AND b = 10 AND c = 5
等条件)。它允许您通过在COLUMNS
子句中使用列列表和在每个PARTITION ... VALUES IN (*
value_list*)
分区定义子句中使用一组列值来指定多列的值。用于
LIST COLUMNS(*
column_list*)
中的列列表和VALUES IN(*
value_list*)
中使用的值列表的数据类型规则与用于RANGE COLUMNS(*
column_list*)
中的列列表和VALUES LESS THAN(*
value_list*)
中使用的值列表的规则相同,只是在VALUES IN
子句中,不允许使用MAXVALUE
,您可以使用NULL
。在使用
PARTITION BY LIST COLUMNS
时,与在使用PARTITION BY LIST
时使用VALUES IN
的值列表之间有一个重要区别。在与PARTITION BY LIST COLUMNS
一起使用时,VALUES IN
子句中的每个元素必须是一组列值;每组中的值数量必须与COLUMNS
子句中使用的列数相同,并且这些值的数据类型必须与列的数据类型匹配(并且以相同顺序出现)。在最简单的情况下,该集合由单个列组成。在column_list
和组成value_list
的元素中可以使用的最大列数为 16。以下
CREATE TABLE
语句定义的表提供了使用LIST COLUMNS
分区的示例:CREATE TABLE lc ( a INT NULL, b INT NULL ) PARTITION BY LIST COLUMNS(a,b) ( PARTITION p0 VALUES IN( (0,0), (NULL,NULL) ), PARTITION p1 VALUES IN( (0,1), (0,2), (0,3), (1,1), (1,2) ), PARTITION p2 VALUES IN( (1,0), (2,0), (2,1), (3,0), (3,1) ), PARTITION p3 VALUES IN( (1,3), (2,2), (2,3), (3,2), (3,3) ) );
-
PARTITIONS *
num*
可以选择使用
PARTITIONS *
num*
子句指定分区的数量,其中num
是分区的数量。如果同时使用此子句和任何使用PARTITION
子句声明的分区,则num
必须等于使用PARTITION
子句声明的所有分区的总数。注意
在创建按
RANGE
或LIST
分区的表时,无论是否使用PARTITIONS
子句,仍必须在表定义中至少包含一个PARTITION VALUES
子句(见下文)。 -
SUBPARTITION BY
分区可以选择地分成多个子分区。这可以通过使用可选的
SUBPARTITION BY
子句来指示。子分区可以通过HASH
或KEY
进行。其中任何一个都可以是LINEAR
。这与先前描述的等效分区类型的工作方式相同。(不可能通过LIST
或RANGE
进行子分区。)可以使用
SUBPARTITIONS
关键字后跟一个整数值来指示子分区的数量。 -
对
PARTITIONS
或SUBPARTITIONS
子句中使用的值进行严格检查,并且此值必须遵守以下规则:-
值必须是正的、非零整数。
-
不允许前导零。
-
值必须是整数文字,并且不能是表达式。例如,
PARTITIONS 0.2E+01
是不允许的,即使0.2E+01
评估为2
。(Bug #15890)
-
-
*
partition_definition*
可以使用
partition_definition
子句分别定义每个分区。构成此子句的各个部分如下:-
PARTITION *
partition_name*
为分区指定一个逻辑名称。
-
VALUES
对于范围分区,每个分区必须包括一个
VALUES LESS THAN
子句;对于列表分区,您必须为每个分区指定一个VALUES IN
子句。这用于确定哪些行将存储在此分区中。有关分区类型的讨论,请参见第二十六章,分区中的语法示例。 -
[STORAGE] ENGINE
MySQL 接受
PARTITION
和SUBPARTITION
的[STORAGE] ENGINE
选项。目前,此选项可用的唯一方式是将所有分区或所有子分区设置为相同的存储引擎,尝试为同一表中的分区或子分区设置不同的存储引擎会引发错误 ERROR 1469 (HY000): The mix of handlers in the partitions is not permitted in this version of MySQL。 -
COMMENT
可以使用可选的
COMMENT
子句来指定描述分区的字符串。示例:COMMENT = 'Data for the years previous to 1999'
分区注释的最大长度为 1024 个字符。
-
DATA DIRECTORY
和INDEX DIRECTORY
DATA DIRECTORY
和INDEX DIRECTORY
可用于指示存储此分区数据和索引的目录。*
data_dir*
和*
index_dir*
必须是绝对系统路径名。截至 MySQL 8.0.21 版本,
DATA DIRECTORY
子句中指定的目录必须为InnoDB
所知。更多信息,请参阅 使用 DATA DIRECTORY 子句。您必须具有
FILE
权限才能使用DATA DIRECTORY
或INDEX DIRECTORY
分区选项。示例:
CREATE TABLE th (id INT, name VARCHAR(30), adate DATE) PARTITION BY LIST(YEAR(adate)) ( PARTITION p1999 VALUES IN (1995, 1999, 2003) DATA DIRECTORY = '/var/appdata/95/data' INDEX DIRECTORY = '/var/appdata/95/idx', PARTITION p2000 VALUES IN (1996, 2000, 2004) DATA DIRECTORY = '/var/appdata/96/data' INDEX DIRECTORY = '/var/appdata/96/idx', PARTITION p2001 VALUES IN (1997, 2001, 2005) DATA DIRECTORY = '/var/appdata/97/data' INDEX DIRECTORY = '/var/appdata/97/idx', PARTITION p2002 VALUES IN (1998, 2002, 2006) DATA DIRECTORY = '/var/appdata/98/data' INDEX DIRECTORY = '/var/appdata/98/idx' );
DATA DIRECTORY
和INDEX DIRECTORY
的行为与用于MyISAM
表的CREATE TABLE
语句的table_option
子句中的行为相同。每个分区可以指定一个数据目录和一个索引目录。如果未指定,数据和索引默认存储在表的数据库目录中。
如果
NO_DIR_IN_CREATE
生效,则在创建分区表时将忽略DATA DIRECTORY
和INDEX DIRECTORY
选项。 -
MAX_ROWS
和MIN_ROWS
可以分别指定要存储在分区中的最大和最小行数。
max_number_of_rows
和min_number_of_rows
的值必须是正整数。与具有相同名称的表级选项一样,这些值仅作为服务器的“建议”,而不是硬限制。 -
TABLESPACE
可以通过指定
TABLESPACE
innodb_file_per_table`` 为分区指定一个InnoDB
每表表空间。所有分区必须属于相同的存储引擎。不支持将
InnoDB
表分区放置在共享的InnoDB
表空间中。共享表空间包括InnoDB
系统表空间和通用表空间。
-
-
*
subpartition_definition*
分区定义可以选择性地包含一个或多个
subpartition_definition
子句。每个子句至少包含SUBPARTITION *
name*
,其中name
是子分区的标识符。除了将PARTITION
关键字替换为SUBPARTITION
外,子分区定义的语法与分区定义完全相同。子分区必须通过
HASH
或KEY
完成,并且只能在RANGE
或LIST
分区上完成。参见第 26.2.6 节,“子分区”。
通过生成列进行分区
允许通过生成的列进行分区。例如:
CREATE TABLE t1 (
s1 INT,
s2 INT AS (EXP(s1)) STORED
)
PARTITION BY LIST (s2) (
PARTITION p1 VALUES IN (1)
);
分区将生成的列视为常规列,这使得可以解决对于分区不允许的函数的限制(参见第 26.6.3 节,“与函数相关的分区限制”)。前面的示例演示了这种技术:EXP()
不能直接在PARTITION BY
子句中使用,但可以使用使用EXP()
定义的生成列。
15.1.20.1 由 CREATE TABLE 创建的文件
对于在文件表空间或通用表空间中创建的InnoDB
表,表数据和相关索引存储在数据库目录中的一个.ibd 文件中。当在系统表空间中创建InnoDB
表时,表数据和索引存储在代表系统表空间的 ibdata*文件中。innodb_file_per_table
选项控制表是在文件表空间还是系统表空间中创建,默认情况下。TABLESPACE
选项可用于将表放置在文件表空间、通用表空间或系统表空间中,而不受innodb_file_per_table
设置的影响。
对于MyISAM
表,存储引擎会创建数据和索引文件。因此,对于每个MyISAM
表tbl_name
,都会有两个磁盘文件。
文件 | 目的 |
---|---|
* tbl_name*.MYD |
数据文件 |
* tbl_name*.MYI |
索引文件 |
第十八章,替代存储引擎,描述了每个存储引擎创建哪些文件来表示表。如果表名包含特殊字符,则表文件的名称将包含这些字符的编码版本,如第 11.2.4 节,“标识符映射到文件名”中所述。
原文:
dev.mysql.com/doc/refman/8.0/en/create-temporary-table.html
15.1.20.2 CREATE TEMPORARY TABLE Statement
在创建表时可以使用 TEMPORARY
关键字。TEMPORARY
表仅在当前会话中可见,并在会话关闭时自动删除。这意味着两个不同的会话可以使用相同的临时表名称而不会与彼此或同名的现有非 TEMPORARY
表发生冲突。(现有表在临时表被删除之前是隐藏的。)
InnoDB
不支持压缩临时表。当启用 innodb_strict_mode
(默认情况下)时,如果指定了 ROW_FORMAT=COMPRESSED
或 KEY_BLOCK_SIZE
,CREATE TEMPORARY TABLE
将返回错误。如果禁用了 innodb_strict_mode
,则会发出警告并使用非压缩行格式创建临时表。innodb_file_per-table
选项不影响创建 InnoDB
临时表。
CREATE TABLE
会导致隐式提交,除非与 TEMPORARY
关键字一起使用。参见 Section 15.3.3, “Statements That Cause an Implicit Commit”。
TEMPORARY
表与数据库(模式)之间的关系非常松散。删除数据库不会自动删除在该数据库中创建的任何 TEMPORARY
表。
要创建临时表,您必须具有 CREATE TEMPORARY TABLES
权限。会话创建临时表后,服务器不会对表执行进一步的权限检查。创建会话可以对表执行任何操作,如 DROP TABLE
、INSERT
、UPDATE
或 SELECT
。
这种行为的一个含义是,一个会话可以操作其临时表,即使当前用户没有创建它们的特权。假设当前用户没有CREATE TEMPORARY TABLES
特权,但能够执行一个以定义者上下文执行且具有CREATE TEMPORARY TABLES
特权的用户权限的存储过程,该存储过程创建了一个临时表。在存储过程执行时,会话使用定义用户的权限。存储过程返回后,有效权限会恢复到当前用户的权限,当前用户仍然可以看到临时表并对其执行任何操作。
你不能使用CREATE TEMPORARY TABLE ... LIKE
来基于mysql
表空间、InnoDB
系统表空间(innodb_system
)或通用表空间中的表的定义创建一个空表。这种表的表空间定义包括一个TABLESPACE
属性,定义了表所在的表空间,而上述表空间不支持临时表。要基于这种表的定义创建一个临时表,使用以下语法:
CREATE TEMPORARY TABLE *new_tbl* SELECT * FROM *orig_tbl* LIMIT 0;
注意
自 MySQL 8.0.13 起,使用CREATE TEMPORARY TABLE
中的TABLESPACE = innodb_file_per_table
和TABLESPACE = innodb_temporary
子句已被弃用;预计在未来的 MySQL 版本中将被移除。
15.1.20.3 CREATE TABLE ... LIKE 语句
使用CREATE TABLE ... LIKE
根据另一个表的定义创建一个空表,包括原始表中定义的任何列属性和索引:
CREATE TABLE *new_tbl* LIKE *orig_tbl*;
复制是使用与原始表相同版本的表存储格式创建的。需要在原始表上具有SELECT
权限。
LIKE
仅适用于基本表,不适用于视图。
重要提示
在执行LOCK TABLES
语句时,不能执行CREATE TABLE
或CREATE TABLE ... LIKE
。
CREATE TABLE ... LIKE
执行与CREATE TABLE
相同的检查。这意味着如果当前的 SQL 模式与创建原始表时生效的模式不同,表定义可能被认为对新模式无效并导致语句失败。
对于CREATE TABLE ... LIKE
,目标表保留原始表中的生成列信息。
对于CREATE TABLE ... LIKE
,目标表保留原始表中的表达式默认值。
对于CREATE TABLE ... LIKE
,目标表保留原始表中的CHECK
约束,只是所有约束名称都是自动生成的。
CREATE TABLE ... LIKE
不保留为原始表指定的任何DATA DIRECTORY
或INDEX DIRECTORY
表选项,也不保留任何外键定义。
如果原始表是临时表,则CREATE TABLE ... LIKE
不保留TEMPORARY
。要创建一个TEMPORARY
目标表,请使用CREATE TEMPORARY TABLE ... LIKE
。
在mysql
表空间、InnoDB
系统表空间(innodb_system
)或通用表空间中创建的表包括表定义中的TABLESPACE
属性,该属性定义了表所在的表空间。由于临时回归,CREATE TABLE ... LIKE
保留了TABLESPACE
属性,并在定义的表空间中创建表,而不管innodb_file_per_table
设置如何。为了在基于此类表的定义创建空表时避免TABLESPACE
属性,请改用以下语法:
CREATE TABLE *new_tbl* SELECT * FROM *orig_tbl* LIMIT 0;
CREATE TABLE ... LIKE
操作将所有ENGINE_ATTRIBUTE
和SECONDARY_ENGINE_ATTRIBUTE
值应用于新表。
15.1.20.4 创建表...选择语句
您可以通过在CREATE TABLE
语句末尾添加一个SELECT
语句来从另一个表创建一个表:
CREATE TABLE *new_tbl* [AS] SELECT * FROM *orig_tbl*;
MySQL 为SELECT
中的所有元素创建新列。例如:
mysql> CREATE TABLE test (a INT NOT NULL AUTO_INCREMENT,
-> PRIMARY KEY (a), KEY(b))
-> ENGINE=InnoDB SELECT b,c FROM test2;
这将创建一个带有三列a
、b
和c
的InnoDB
表。ENGINE
选项是CREATE TABLE
语句的一部分,不应在SELECT
之后使用;否则会导致语法错误。其他CREATE TABLE
选项如CHARSET
也是如此。
注意,SELECT
语句中的列附加在表的右侧,而不是重叠在其上。看下面的例子:
mysql> SELECT * FROM foo;
+---+
| n |
+---+
| 1 |
+---+
mysql> CREATE TABLE bar (m INT) SELECT n FROM foo;
Query OK, 1 row affected (0.02 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM bar;
+------+---+
| m | n |
+------+---+
| NULL | 1 |
+------+---+
1 row in set (0.00 sec)
对于表foo
中的每一行,在bar
中插入一行,其值来自foo
并且新列使用默认值。
在由CREATE TABLE ... SELECT
生成的表中,仅在CREATE TABLE
部分命名的列首先出现。在两个部分或仅在SELECT
部分命名的列在其后出现。SELECT
列的数据类型可以通过在CREATE TABLE
部分中指定该列来覆盖。
如果在将数据复制到表时发生错误,则表会自动删除而不会创建。然而,在 MySQL 8.0.21 之前,当使用基于行的复制时,CREATE TABLE ... SELECT
语句会在二进制日志中记录为两个事务,一个用于创建表,另一个用于插入数据。当从二进制日志应用语句时,在两个事务之间或在复制数据时发生故障可能导致复制空表。这个限制在 MySQL 8.0.21 中被移除。在支持原子 DDL 的存储引擎上,当使用基于行的复制时,CREATE TABLE ... SELECT
现在被记录并应用为一个事务。更多信息,请参见第 15.1.1 节,“原子数据定义语句支持”。
截至 MySQL 8.0.21,在支持原子 DDL 和外键约束的存储引擎上,在使用基于行的复制时,不允许在CREATE TABLE ... SELECT
语句中创建外键。可以稍后使用ALTER TABLE
添加外键约束。
您可以在SELECT
之前加上IGNORE
或REPLACE
来指示如何处理重复唯一键值的行。使用IGNORE
,重复唯一键值的行将被丢弃。使用REPLACE
,新行将替换具有相同唯一键值的行。如果未指定IGNORE
或REPLACE
,重复的唯一键值将导致错误。有关更多信息,请参见 IGNORE 对语句执行的影响。
在 MySQL 8.0.19 及更高版本中,您还可以在CREATE TABLE ... SELECT
语句的SELECT
部分使用VALUES
语句;VALUES
语句的部分必须包含使用AS
子句的表别名。为了命名来自VALUES
的列,使用表别名提供列别名;否则,默认列名column_0
、column_1
、column_2
等将被使用。
否则,在创建的表中列的命名遵循本节中先前描述的相同规则。示例:
mysql> CREATE TABLE tv1
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v;
mysql> TABLE tv1;
+----------+----------+----------+
| column_0 | column_1 | column_2 |
+----------+----------+----------+
| 1 | 3 | 5 |
| 2 | 4 | 6 |
+----------+----------+----------+
mysql> CREATE TABLE tv2
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
mysql> TABLE tv2;
+---+---+---+
| x | y | z |
+---+---+---+
| 1 | 3 | 5 |
| 2 | 4 | 6 |
+---+---+---+
mysql> CREATE TABLE tv3 (a INT, b INT, c INT)
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
mysql> TABLE tv3;
+------+------+------+----------+----------+----------+
| a | b | c | x | y | z |
+------+------+------+----------+----------+----------+
| NULL | NULL | NULL | 1 | 3 | 5 |
| NULL | NULL | NULL | 2 | 4 | 6 |
+------+------+------+----------+----------+----------+
mysql> CREATE TABLE tv4 (a INT, b INT, c INT)
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
mysql> TABLE tv4;
+------+------+------+---+---+---+
| a | b | c | x | y | z |
+------+------+------+---+---+---+
| NULL | NULL | NULL | 1 | 3 | 5 |
| NULL | NULL | NULL | 2 | 4 | 6 |
+------+------+------+---+---+---+
mysql> CREATE TABLE tv5 (a INT, b INT, c INT)
> SELECT * FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(a,b,c);
mysql> TABLE tv5;
+------+------+------+
| a | b | c |
+------+------+------+
| 1 | 3 | 5 |
| 2 | 4 | 6 |
+------+------+------+
当选择所有列并使用默认列名时,可以省略SELECT *
,因此刚刚用于创建表tv1
的语句也可以写成如下所示:
mysql> CREATE TABLE tv1 VALUES ROW(1,3,5), ROW(2,4,6);
mysql> TABLE tv1;
+----------+----------+----------+
| column_0 | column_1 | column_2 |
+----------+----------+----------+
| 1 | 3 | 5 |
| 2 | 4 | 6 |
+----------+----------+----------+
当使用VALUES
作为SELECT
的源时,所有列始终被选入新表中,无法像从命名表中选择时那样选择单独的列;以下每个语句都会产生错误(ER_OPERAND_COLUMNS
):
CREATE TABLE tvx
SELECT (x,z) FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
CREATE TABLE tvx (a INT, c INT)
SELECT (x,z) FROM (VALUES ROW(1,3,5), ROW(2,4,6)) AS v(x,y,z);
类似地,您可以在SELECT
语句的位置使用TABLE
语句。这遵循与VALUES
相同的规则;源表的所有列及其在源表中的名称始终被插入到新表中。示例:
mysql> TABLE t1;
+----+----+
| a | b |
+----+----+
| 1 | 2 |
| 6 | 7 |
| 10 | -4 |
| 14 | 6 |
+----+----+
mysql> CREATE TABLE tt1 TABLE t1;
mysql> TABLE tt1;
+----+----+
| a | b |
+----+----+
| 1 | 2 |
| 6 | 7 |
| 10 | -4 |
| 14 | 6 |
+----+----+
mysql> CREATE TABLE tt2 (x INT) TABLE t1;
mysql> TABLE tt2;
+------+----+----+
| x | a | b |
+------+----+----+
| NULL | 1 | 2 |
| NULL | 6 | 7 |
| NULL | 10 | -4 |
| NULL | 14 | 6 |
+------+----+----+
由于底层SELECT
语句中行的排序不能总是确定,CREATE TABLE ... IGNORE SELECT
和CREATE TABLE ... REPLACE SELECT
语句被标记为不安全的基于语句的复制。在使用基于语句的模式时,这些语句在错误日志中产生警告,并在使用MIXED
模式时以基于行的格式写入二进制日志。另请参阅第 19.2.1.1 节,“基于语句和基于行的复制的优缺点”。
CREATE TABLE ... SELECT
不会为您自动创建任何索引。这是有意为之,以使语句尽可能灵活。如果您希望在创建的表中有索引,您应该在SELECT
语句之前指定这些索引:
mysql> CREATE TABLE bar (UNIQUE (n)) SELECT n FROM foo;
对于CREATE TABLE ... SELECT
,目标表不会保留所选自表中列是否为生成列的信息。语句的SELECT
部分不能为目标表中的生成列分配值。
对于CREATE TABLE ... SELECT
,目标表会保留原始表中的表达式默认值。
可能会发生一些数据类型的转换。例如,AUTO_INCREMENT
属性不会被保留,VARCHAR
列可能会变成CHAR
列。保留的属性包括NULL
(或NOT NULL
)以及对于那些具有的列,CHARACTER SET
,COLLATION
,COMMENT
和DEFAULT
子句。
在使用CREATE TABLE ... SELECT
创建表时,请确保为查询中的任何函数调用或表达式设置别名。如果不这样做,CREATE
语句可能会失败或导致不良的列名称。
CREATE TABLE artists_and_works
SELECT artist.name, COUNT(work.artist_id) AS number_of_works
FROM artist LEFT JOIN work ON artist.id = work.artist_id
GROUP BY artist.id;
您还可以明确指定创建表中列的数据类型:
CREATE TABLE foo (a TINYINT NOT NULL) SELECT b+1 AS a FROM bar;
对于CREATE TABLE ... SELECT
,如果指定了IF NOT EXISTS
并且目标表存在,则不会将任何内容插入目标表,并且该语句不会被记录。
为了确保二进制日志可以用于重新创建原始表,MySQL 不允许在 CREATE TABLE ... SELECT
过程中进行并发插入。然而,在 MySQL 8.0.21 之前,当使用基于行的复制时,从二进制日志应用 CREATE TABLE ... SELECT
操作时,允许在复制表上进行并发插入。这个限制在支持原子 DDL 的存储引擎上在 MySQL 8.0.21 中被移除。更多信息,请参见 Section 15.1.1, “Atomic Data Definition Statement Support”。
你不能在类似 CREATE TABLE *
new_table* SELECT ... FROM *
old_table* ...
的语句中将 FOR UPDATE
作为 SELECT
的一部分。如果尝试这样做,该语句将失败。
CREATE TABLE ... SELECT
操作仅对列应用 ENGINE_ATTRIBUTE
和 SECONDARY_ENGINE_ATTRIBUTE
值。除非明确指定,否则表和索引的 ENGINE_ATTRIBUTE
和 SECONDARY_ENGINE_ATTRIBUTE
值不会应用于新表。
原文:
dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html
15.1.20.5 外键约束
MySQL 支持外键,允许在表之间进行相关数据的交叉引用,并支持外键约束,有助于保持相关数据的一致性。
外键关系涉及一个包含初始列值的父表,以及一个包含引用父列值的列值的子表。外键约束定义在子表上。
在CREATE TABLE
或ALTER TABLE
语句中定义外键约束的基本语法包括以下内容:
[CONSTRAINT [*symbol*]] FOREIGN KEY
[*index_name*] (*col_name*, ...)
REFERENCES *tbl_name* (*col_name*,...)
[ON DELETE *reference_option*]
[ON UPDATE *reference_option*]
*reference_option*:
RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT
外键约束的使用方法在本节的以下主题中描述:
-
标识符
-
条件和限制
-
参考动作
-
外键约束示例
-
添加外键约束
-
删除外键约束
-
外键检查
-
锁定
-
外键定义和元数据
-
外键错误
标识符
外键约束命名受以下规则约束:
-
如果未定义
CONSTRAINT
symbol
子句,或在CONSTRAINT
关键字后未包含符号,则会自动生成约束名。 -
如果未定义
CONSTRAINT
symbol
子句,或在CONSTRAINT
关键字后未包含符号,则会自动生成约束名。在 MySQL 8.0.16 之前,如果未定义
CONSTRAINT
symbol
子句,或在CONSTRAINT
关键字后未包含符号,则InnoDB
和NDB
存储引擎将使用FOREIGN_KEY *
index_name*
(如果已定义)。在 MySQL 8.0.16 及更高版本中,将忽略FOREIGN_KEY *
index_name*
。 -
如果定义了
CONSTRAINT *
symbol*
值,则必须在数据库中是唯一的。重复的symbol
会导致类似以下错误:ERROR 1005 (HY000): 无法创建表'test.fk1'(错误号:121)。 -
NDB 集群使用创建时的相同大小写存储外键名称。在版本 8.0.20 之前,当处理
SELECT
和其他 SQL 语句时,NDB
将这些语句中的外键名称与存储的名称进行比较,当lower_case_table_names
等于 0 时,以区分大小写的方式存储。在 NDB 8.0.20 及更高版本中,此值不再影响这些比较是如何进行的,它们总是不考虑大小写地进行。 (Bug #30512043)
在 FOREIGN KEY ... REFERENCES
子句中的表格和列标识符可以在反引号内引用 (```sql). Alternatively, double quotation marks ("
) can be used if the ANSI_QUOTES
SQL mode is enabled. The lower_case_table_names
system variable setting is also taken into account.
Conditions and Restrictions
Foreign key constraints are subject to the following conditions and restrictions:
-
Parent and child tables must use the same storage engine, and they cannot be defined as temporary tables.
-
Creating a foreign key constraint requires the
REFERENCES
privilege on the parent table. -
Corresponding columns in the foreign key and the referenced key must have similar data types. The size and sign of fixed precision types such as
INTEGER
- INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT") andDECIMAL
- DECIMAL, NUMERIC") must be the same. The length of string types need not be the same. For nonbinary (character) string columns, the character set and collation must be the same. -
MySQL supports foreign key references between one column and another within a table. (A column cannot have a foreign key reference to itself.) In these cases, a “child table record” refers to a dependent record within the same table.
-
MySQL requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan. In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order. Such an index is created on the referencing table automatically if it does not exist. This index might be silently dropped later if you create another index that can be used to enforce the foreign key constraint.
index_name
, if given, is used as described previously. -
InnoDB
permits a foreign key to reference any index column or group of columns. However, in the referenced table, there must be an index where the referenced columns are the first columns in the same order. Hidden columns thatInnoDB
adds to an index are also considered (see Section 17.6.2.1, “Clustered and Secondary Indexes”).NDB
requires an explicit unique key (or primary key) on any column referenced as a foreign key.InnoDB
does not, which is an extension of standard SQL. -
Index prefixes on foreign key columns are not supported. Consequently,
BLOB
andTEXT
columns cannot be included in a foreign key because indexes on those columns must always include a prefix length. -
InnoDB
does not currently support foreign keys for tables with user-defined partitioning. This includes both parent and child tables.This restriction does not apply for
NDB
tables that are partitioned byKEY
orLINEAR KEY
(the only user partitioning types supported by theNDB
storage engine); these may have foreign key references or be the targets of such references. -
A table in a foreign key relationship cannot be altered to use another storage engine. To change the storage engine, you must drop any foreign key constraints first.
-
A foreign key constraint cannot reference a virtual generated column.
For information about how the MySQL implementation of foreign key constraints differs from the SQL standard, see Section 1.6.2.3, “FOREIGN KEY Constraint Differences”.
Referential Actions
When an UPDATE
or DELETE
operation affects a key value in the parent table that has matching rows in the child table, the result depends on the referential action specified by ON UPDATE
and ON DELETE
subclauses of the FOREIGN KEY
clause. Referential actions include:
-
CASCADE
: Delete or update the row from the parent table and automatically delete or update the matching rows in the child table. BothON DELETE CASCADE
andON UPDATE CASCADE
are supported. Between two tables, do not define severalON UPDATE CASCADE
clauses that act on the same column in the parent table or in the child table.If a
FOREIGN KEY
clause is defined on both tables in a foreign key relationship, making both tables a parent and child, anON UPDATE CASCADE
orON DELETE CASCADE
subclause defined for oneFOREIGN KEY
clause must be defined for the other in order for cascading operations to succeed. If anON UPDATE CASCADE
orON DELETE CASCADE
subclause is only defined for oneFOREIGN KEY
clause, cascading operations fail with an error.Note
Cascaded foreign key actions do not activate triggers.
-
SET NULL
: Delete or update the row from the parent table and set the foreign key column or columns in the child table toNULL
. BothON DELETE SET NULL
andON UPDATE SET NULL
clauses are supported.If you specify a
SET NULL
action, make sure that you have not declared the columns in the child table asNOT NULL
. -
RESTRICT
: Rejects the delete or update operation for the parent table. SpecifyingRESTRICT
(orNO ACTION
) is the same as omitting theON DELETE
orON UPDATE
clause. -
NO ACTION
: A keyword from standard SQL. ForInnoDB
, this is equivalent toRESTRICT
; the delete or update operation for the parent table is immediately rejected if there is a related foreign key value in the referenced table.NDB
supports deferred checks, andNO ACTION
specifies a deferred check; when this is used, constraint checks are not performed until commit time. Note that forNDB
tables, this causes all foreign key checks made for both parent and child tables to be deferred. -
SET DEFAULT
: This action is recognized by the MySQL parser, but bothInnoDB
andNDB
reject table definitions containingON DELETE SET DEFAULT
orON UPDATE SET DEFAULT
clauses.
For storage engines that support foreign keys, MySQL rejects any INSERT
or UPDATE
operation that attempts to create a foreign key value in a child table if there is no matching candidate key value in the parent table.
For an ON DELETE
or ON UPDATE
that is not specified, the default action is always NO ACTION
.
As the default, an ON DELETE NO ACTION
or ON UPDATE NO ACTION
clause that is specified explicitly does not appear in SHOW CREATE TABLE
output or in tables dumped with mysqldump. RESTRICT
, which is an equivalent non-default keyword, appears in SHOW CREATE TABLE
output and in tables dumped with mysqldump.
For NDB
tables, ON UPDATE CASCADE
is not supported where the reference is to the parent table's primary key.
As of NDB 8.0.16: For NDB
tables, ON DELETE CASCADE
is not supported where the child table contains one or more columns of any of the TEXT
or BLOB
types. (Bug #89511, Bug #27484882)
InnoDB
performs cascading operations using a depth-first search algorithm on the records of the index that corresponds to the foreign key constraint.
A foreign key constraint on a stored generated column cannot use CASCADE
, SET NULL
, or SET DEFAULT
as ON UPDATE
referential actions, nor can it use SET NULL
or SET DEFAULT
as ON DELETE
referential actions.
A foreign key constraint on the base column of a stored generated column cannot use CASCADE
, SET NULL
, or SET DEFAULT
as ON UPDATE
or ON DELETE
referential actions.
Foreign Key Constraint Examples
This simple example relates parent
and child
tables through a single-column foreign key:
创建表格父 (
id INT 不为空,
主键 (id)
) 引擎=INNODB;
创建表格子表 (
id INT,
parent_id INT,
索引 par_ind (parent_id),
外键 (parent_id)
REFERENCES 父(id)
在删除时级联
) 引擎=INNODB;
```sql
This is a more complex example in which a `product_order` table has foreign keys for two other tables. One foreign key references a two-column index in the `product` table. The other references a single-column index in the `customer` table:
创建表格产品 (
category INT NOT NULL, id INT NOT NULL,
价格 DECIMAL,
主键(category, id)
) 引擎=INNODB;
创建表格顾客 (
id INT 不为空,
主键 (id)
) 引擎=INNODB;
创建表格产品订单 (
no INT 不为空 自动增量,
产品类别 INT 不为空,
product_id INT NOT NULL,
顾客 id INT 不为空,
主键(no),
索引 (产品类别, 产品 id),
索引 (顾客 id),
外键 (产品类别, 产品 id)
REFERENCES 产品(类别, id)
在更新时级联在删除时限制,
外键 (customer_id)
REFERENCES 顾客(id)
) 引擎=INNODB;
##### Adding Foreign Key Constraints
You can add a foreign key constraint to an existing table using the following `ALTER TABLE` syntax:
ALTER TABLE tbl_name
添加 [约束 [*symbol*]] 外键
[*index_name*] (*col_name*, ...)
REFERENCES *tbl_name* (*col_name*,...)
[ON DELETE *reference_option*]
[在更新 *reference_option*]
The foreign key can be self referential (referring to the same table). When you add a foreign key constraint to a table using `ALTER TABLE`, *remember to first create an index on the column(s) referenced by the foreign key.*
##### Dropping Foreign Key Constraints
You can drop a foreign key constraint using the following `ALTER TABLE` syntax:
ALTER TABLE tbl_name DROP FOREIGN KEY fk_symbol;
If the `FOREIGN KEY` clause defined a `CONSTRAINT` name when you created the constraint, you can refer to that name to drop the foreign key constraint. Otherwise, a constraint name was generated internally, and you must use that value. To determine the foreign key constraint name, use `SHOW CREATE TABLE`:
mysql> SHOW CREATE TABLE child\G
*************************** 1. 行 ***************************
表格:子表
创建表格:CREATE TABLE child
(
id
int DEFAULT NULL,
parent_id
int DEFAULT NULL,
键 par_ind
(parent_id
),
CONSTRAINT child_ibfk_1
FOREIGN KEY (parent_id
)
REFERENCES parent
(id
) ON DELETE CASCADE
) 引擎=InnoDB 默认字符集=utf8mb4 校对=utf8mb4_0900_ai_ci
mysql> ALTER TABLE child DROP FOREIGN KEY child_ibfk_1
;
Adding and dropping a foreign key in the same `ALTER TABLE` statement is supported for `ALTER TABLE ... ALGORITHM=INPLACE`. It is not supported for `ALTER TABLE ... ALGORITHM=COPY`.
##### Foreign Key Checks
In MySQL, InnoDB and NDB tables support checking of foreign key constraints. Foreign key checking is controlled by the `foreign_key_checks` variable, which is enabled by default. Typically, you leave this variable enabled during normal operation to enforce referential integrity. The `foreign_key_checks` variable has the same effect on `NDB` tables as it does for `InnoDB` tables.
The `foreign_key_checks` variable is dynamic and supports both global and session scopes. For information about using system variables, see Section 7.1.9, “Using System Variables”.
Disabling foreign key checking is useful when:
* Dropping a table that is referenced by a foreign key constraint. A referenced table can only be dropped after `foreign_key_checks` is disabled. When you drop a table, constraints defined on the table are also dropped.
* Reloading tables in different order than required by their foreign key relationships. For example, **mysqldump** produces correct definitions of tables in the dump file, including foreign key constraints for child tables. To make it easier to reload dump files for tables with foreign key relationships, **mysqldump** automatically includes a statement in the dump output that disables `foreign_key_checks`. This enables you to import the tables in any order in case the dump file contains tables that are not correctly ordered for foreign keys. Disabling `foreign_key_checks` also speeds up the import operation by avoiding foreign key checks.
* Executing `LOAD DATA` operations, to avoid foreign key checking.
* Performing an `ALTER TABLE` operation on a table that has a foreign key relationship.
When `foreign_key_checks` is disabled, foreign key constraints are ignored, with the following exceptions:
* Recreating a table that was previously dropped returns an error if the table definition does not conform to the foreign key constraints that reference the table. The table must have the correct column names and types. It must also have indexes on the referenced keys. If these requirements are not satisfied, MySQL returns Error 1005 that refers to errno: 150 in the error message, which means that a foreign key constraint was not correctly formed.
* Altering a table returns an error (errno: 150) if a foreign key definition is incorrectly formed for the altered table.
* Dropping an index required by a foreign key constraint. The foreign key constraint must be removed before dropping the index.
* Creating a foreign key constraint where a column references a nonmatching column type.
Disabling `foreign_key_checks` has these additional implications:
* It is permitted to drop a database that contains tables with foreign keys that are referenced by tables outside the database.
* It is permitted to drop a table with foreign keys referenced by other tables.
* Enabling `foreign_key_checks` does not trigger a scan of table data, which means that rows added to a table while `foreign_key_checks` is disabled are not checked for consistency when `foreign_key_checks` is re-enabled.
##### Locking
MySQL extends metadata locks, as necessary, to tables that are related by a foreign key constraint. Extending metadata locks prevents conflicting DML and DDL operations from executing concurrently on related tables. This feature also enables updates to foreign key metadata when a parent table is modified. In earlier MySQL releases, foreign key metadata, which is owned by the child table, could not be updated safely.
If a table is locked explicitly with `LOCK TABLES`, any tables related by a foreign key constraint are opened and locked implicitly. For foreign key checks, a shared read-only lock (`LOCK TABLES READ`) is taken on related tables. For cascading updates, a shared-nothing write lock (`LOCK TABLES WRITE`) is taken on related tables that are involved in the operation.
##### Foreign Key Definitions and Metadata
To view a foreign key definition, use `SHOW CREATE TABLE`:
mysql> SHOW CREATE TABLE child\G
*************************** 1. 行 ***************************
表格:子表
创建表格:CREATE TABLE child
(
id
int DEFAULT NULL,
parent_id
int DEFAULT NULL,
键 par_ind
(parent_id
),
CONSTRAINT child_ibfk_1
FOREIGN KEY (parent_id
)
REFERENCES parent
(id
) ON DELETE CASCADE
) 引擎=InnoDB 默认字符集=utf8mb4 校对=utf8mb4_0900_ai_ci
You can obtain information about foreign keys from the Information Schema `KEY_COLUMN_USAGE` table. An example of a query against this table is shown here:
mysql> 选择 表格模式, 表格名称, 列名, 约束名称
来自 INFORMATION_SCHEMA.KEY_COLUMN_USAGE
当引用表格模式不为空时;
+--------------+------------+-------------+-----------------+
表格模式 | 表格名称 | 列名 | 约束名称 |
---|
+--------------+------------+-------------+-----------------+
test | child | parent_id | child_ibfk_1 |
---|
+--------------+------------+-------------+-----------------+
You can obtain information specific to `InnoDB` foreign keys from the `INNODB_FOREIGN` and `INNODB_FOREIGN_COLS` tables. Example queries are show here:
mysql> 从 INFORMATION_SCHEMA.INNODB_FOREIGN 表中选择 * \G
*************************** 1. 行 ***************************
ID: test/child_ibfk_1
FOR_NAME: test/child
REF_NAME: test/parent
N_COLS: 1
类型: 1
mysql> 从 INFORMATION_SCHEMA.INNODB_FOREIGN_COLS 表中选择 * \G
*************************** 1. 行 ***************************
ID: test/child_ibfk_1
FOR_COL_NAME: parent_id
REF_COL_NAME: id
位置: 0
##### Foreign Key Errors
In the event of a foreign key error involving `InnoDB` tables (usually Error 150 in the MySQL Server), information about the latest foreign key error can be obtained by checking `SHOW ENGINE INNODB STATUS` output.
mysql> 显示引擎 INNODB 状态\G
...
最新的外键错误
2018-04-12 14:57:24 0x7f97a9c91700 事务:
事务 7717,活跃 0 秒插入
使用中的 mysql 表 1,已锁定 1
4 个锁结构,堆大小 1136,3 个行锁,撤销日志条目 3
MySQL 线程 id 8,OS 线程句柄 140289365317376,查询 id 14 localhost root 更新
插入到 child 表中的值 (NULL, 1), (NULL, 2), (NULL, 3), (NULL, 4), (NULL, 5), (NULL, 6)
对于表 test
.child
的外键约束失败:
,
约束 child_ibfk_1
外键 (parent_id
) 引用 parent
表 (id
) 在删除时
级联更新级联
尝试在子表中添加,索引 par_ind 元组:
数据元组: 2 个字段;
0: 长度 4; 十六进制 80000003; ASCII ;;
1: 长度 4; 十六进制 80000003; ASCII ;;
但在父表 test
.parent
中,在主键 PRIMARY 中,
我们能找到的最接近的匹配是记录:
物理记录: 字段数 3; 紧凑格式; 信息位 0
0: 长度 4; 十六进制 80000004; ASCII ;;
1: 长度 6; 十六进制 000000001e19; ASCII ;;
2: 长度 7; 十六进制 81000001110137; ASCII 7;;
...
警告
如果用户对所有父表具有表级别权限,则外键操作的 `ER_NO_REFERENCED_ROW_2` 和 `ER_ROW_IS_REFERENCED_2` 错误消息会暴露有关父表的信息。如果用户对所有父表没有表级别权限,则显示更通用的错误消息 (`ER_NO_REFERENCED_ROW` 和 `ER_ROW_IS_REFERENCED`)。
一个例外是,对于定义为使用 `DEFINER` 权限执行的存储程序,权限评估的用户是程序中 `DEFINER` 子句中的用户,而不是调用用户。如果该用户具有表级别父表权限,父表信息仍然会显示。在这种情况下,存储程序创建者有责任通过包含适当的条件处理程序来隐藏信息。
> 原文:[`dev.mysql.com/doc/refman/8.0/en/create-table-check-constraints.html`](https://dev.mysql.com/doc/refman/8.0/en/create-table-check-constraints.html)
#### 15.1.20.6 CHECK 约束
在 MySQL 8.0.16 之前,`CREATE TABLE`仅允许以下有限版本的表`CHECK`约束语法,该语法被解析并忽略:
```sql
CHECK (*expr*)
截至 MySQL 8.0.16,CREATE TABLE
允许所有存储引擎的表和列CHECK
约束的核心特性。CREATE TABLE
允许以下CHECK
约束语法,适用于表约束和列约束:
[CONSTRAINT [*symbol*]] CHECK (*expr*) [[NOT] ENFORCED]
可选的symbol
指定了约束的名称。如果省略,MySQL 会从表名、字面量_chk_
和一个序号(1、2、3、...)生成一个名称。约束名称最长为 64 个字符。它们区分大小写,但不区分重音符号。
expr
指定约束条件为一个布尔表达式,每行必须评估为TRUE
或UNKNOWN
(对于NULL
值)。如果条件评估为FALSE
,则失败并发生约束违反。违反的效果取决于正在执行的语句,如本节后面所述。
可选的强制执行子句指示约束是否被执行:
-
如果省略或指定为
ENFORCED
,则创建并执行约束。 -
如果指定为
NOT ENFORCED
,则创建约束但不执行。
CHECK
约束可以指定为表约束或列约束:
-
表约束不出现在列定义中,可以引用任何表列或列。允许对稍后出现在表定义中的列进行前向引用。
-
列约束出现在列定义中,只能引用该列。
考虑这个表定义:
CREATE TABLE t1
(
CHECK (c1 <> c2),
c1 INT CHECK (c1 > 10),
c2 INT CONSTRAINT c2_positive CHECK (c2 > 0),
c3 INT CHECK (c3 < 100),
CONSTRAINT c1_nonzero CHECK (c1 <> 0),
CHECK (c1 > c3)
);
定义包括命名和未命名格式的表约束和列约束:
-
第一个约束��表约束:它出现在任何列定义之外,因此可以(并且确实)引用多个表列。此约束包含对尚未定义的列的前向引用。未指定约束名称,因此 MySQL 生成一个名称。
-
接下来的三个约束是列约束:每个出现在列定义中,因此只能引用正在定义的列。其中一个约束明确命名。MySQL 为另外两个生成名称。
-
最后两个约束是表约束。其中一个明确命名。MySQL 为另一个生成一个名称。
如前所述,MySQL 为未指定名称的任何CHECK
约束生成一个名称。要查看前述表定义生成的名称,请使用SHOW CREATE TABLE
:
mysql> SHOW CREATE TABLE t1\G
*************************** 1\. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`c1` int(11) DEFAULT NULL,
`c2` int(11) DEFAULT NULL,
`c3` int(11) DEFAULT NULL,
CONSTRAINT `c1_nonzero` CHECK ((`c1` <> 0)),
CONSTRAINT `c2_positive` CHECK ((`c2` > 0)),
CONSTRAINT `t1_chk_1` CHECK ((`c1` <> `c2`)),
CONSTRAINT `t1_chk_2` CHECK ((`c1` > 10)),
CONSTRAINT `t1_chk_3` CHECK ((`c3` < 100)),
CONSTRAINT `t1_chk_4` CHECK ((`c1` > `c3`))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
SQL 标准规定所有类型的约束(主键,唯一索引,外键,检查)属于同一命名空间。在 MySQL 中,每种约束类型在每个模式(数据库)中都有自己的命名空间。因此,CHECK
约束名称必须在每个模式中是唯一的;同一模式中的两个表不能共享CHECK
约束名称。(例外情况:TEMPORARY
表隐藏了同名的非TEMPORARY
表,因此它也可以具有相同的CHECK
约束名称。)
以表名开头生成的约束名称有助于确保模式的唯一性,因为表名在模式内也必须是唯一的。
CHECK
条件表达式必须遵守以下规则。如果表达式包含不允许的结构,则会发生错误。
-
允许非生成和生成列,除了具有
AUTO_INCREMENT
属性的列和其他表中的列。 -
允许使用文字,确定性内置函数和运算符。如果给定表中的数据相同,则函数是确定性的,多次调用会产生相同的结果,与连接的用户无关。不符合此定义的函数的示例包括:
CONNECTION_ID()
,CURRENT_USER()
,NOW()
。 -
不允许存储函数和可加载函数。
-
不允许存储过程和函数参数。
-
变量(系统变量,用户定义变量和存储程序本地变量)是不允许的。
-
不允许子查询。
外键参照操作(ON UPDATE
,ON DELETE
)在用于CHECK
约束的列上是被禁止的。同样,CHECK
约束在用于外键参照操作的列上也是被禁止的。
CHECK
约束会在INSERT
,UPDATE
,REPLACE
,LOAD DATA
和LOAD XML
语句中进行评估,如果约束评估为FALSE
,则会发生错误。如果发生错误,已应用更改的处理方式对于事务性和非事务性存储引擎有所不同,并且还取决于是否启用了严格的 SQL 模式,如严格的 SQL 模式中所述。
CHECK
约束会在INSERT IGNORE
,UPDATE IGNORE
,LOAD DATA ... IGNORE
和LOAD XML ... IGNORE
语句中进行评估,如果约束评估为FALSE
,则会发出警告。对于任何违反约束的行,插入或更新将被跳过。
如果约束表达式评估为与声明的列类型不同的数据类型,则根据通常的 MySQL 类型转换规则发生对声明类型的隐式强制转换。请参阅第 14.3 节,“表达式评估中的类型转换”。如果类型转换失败或导致精度丢失,则会发生错误。
注意
约束表达式评估在评估时使用当前的 SQL 模式。如果表达式的任何组件依赖于 SQL 模式,则除非在所有使用期间 SQL 模式相同,否则对表的不同使用可能导致不同的结果。
信息模式CHECK_CONSTRAINTS
表提供有关在表上定义的 CHECK
约束的信息。请参阅第 28.3.5 节,“INFORMATION_SCHEMA CHECK_CONSTRAINTS 表”。
原文:
dev.mysql.com/doc/refman/8.0/en/silent-column-changes.html
15.1.20.7 悄悄更改列规范
在某些情况下,MySQL 会悄悄地更改列规范,使其与 CREATE TABLE
或 ALTER TABLE
语句中给出的规范不同。这些更改可能是对数据类型的更改,对与数据类型相关联的属性的更改,或对索引规范的更改。
所有更改都受到 65,535 字节的内部行大小限制的影响,这可能导致某些数据类型更改尝试失败。参见 第 10.4.7 节,“表列数和行大小限制”。
-
PRIMARY KEY
的一部分列即使没有声明为NOT NULL
也会被设置为NOT NULL
。 -
在创建表时,
ENUM
和SET
成员值的尾随空格会被自动删除。 -
MySQL 将其他 SQL 数据库供应商使用的某些数据类型映射到 MySQL 类型。参见 第 13.9 节,“使用其他数据库引擎的数据类型”。
-
如果您包含一个
USING
子句来指定对于给定存储引擎不允许的索引类型,但是有另一种可用的索引类型,该引擎可以在不影响查询结果的情况下使用该索引类型�� -
如果未启用严格的 SQL 模式,则长度大于 65535 的
VARCHAR
列会转换为TEXT
,长度大于 65535 的VARBINARY
列会转换为BLOB
。否则,在这两种情况下都会发生错误。 -
为字符数据类型指定
CHARACTER SET binary
属性会导致列被创建为相应的二进制数据类型:CHAR
变为BINARY
,VARCHAR
变为VARBINARY
,TEXT
变为BLOB
。对于ENUM
和SET
数据类型,不会发生这种情况;它们会按照声明创建。假设您使用以下定义指定表:CREATE TABLE t ( c1 VARCHAR(10) CHARACTER SET binary, c2 TEXT CHARACTER SET binary, c3 ENUM('a','b','c') CHARACTER SET binary );
结果表的定义如下:
CREATE TABLE t ( c1 VARBINARY(10), c2 BLOB, c3 ENUM('a','b','c') CHARACTER SET binary );
若要查看 MySQL 是否使用了您指定之外的数据类型,请在创建或更改表后发出DESCRIBE
或SHOW CREATE TABLE
语句。
如果使用myisampack对表进行压缩,可能会发生某些其他数据类型的更改。请参阅第 18.2.3.3 节,“压缩表特性”。
原文:
dev.mysql.com/doc/refman/8.0/en/create-table-generated-columns.html
15.1.20.8 CREATE TABLE and Generated Columns
CREATE TABLE
支持生成列的规范。生成列的值是从列定义中包含的表达式计算出来的。
生成列也受到NDB
存储引擎的支持。
以下简单示例显示了一个表,该表存储直角三角形的边长在sidea
和sideb
列中,并在sidec
中计算斜边的长度(其他两边平方和的平方根):
CREATE TABLE triangle (
sidea DOUBLE,
sideb DOUBLE,
sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb))
);
INSERT INTO triangle (sidea, sideb) VALUES(1,1),(3,4),(6,8);
从表中选择会产生以下结果:
mysql> SELECT * FROM triangle;
+-------+-------+--------------------+
| sidea | sideb | sidec |
+-------+-------+--------------------+
| 1 | 1 | 1.4142135623730951 |
| 3 | 4 | 5 |
| 6 | 8 | 10 |
+-------+-------+--------------------+
使用triangle
表的任何应用程序都可以访问斜边值,而无需指定计算它们的表达式。
生成列的定义具有以下语法:
*col_name* *data_type* [GENERATED ALWAYS] AS (*expr*)
[VIRTUAL | STORED] [NOT NULL | NULL]
[UNIQUE [KEY]] [[PRIMARY] KEY]
[COMMENT '*string*']
AS (*
expr*)
表示该列是生成的,并定义用于计算列值的表达式。AS
之前可以加上GENERATED ALWAYS
以使列的生成性质更加明确。表达式中允许或禁止的结构将在后面讨论。
VIRTUAL
或STORED
关键字指示列值的存储方式,这对列的使用有影响:
-
VIRTUAL
:列值不存储,但在读取行时立即计算,紧随任何BEFORE
触发器之后。虚拟列不占用存储空间。InnoDB
支持虚拟列上的二级索引。请参阅 Section 15.1.20.9, “Secondary Indexes and Generated Columns”。 -
STORED
:在插入或更新行时,列值会被计算并存储。存储列确实需要存储空间,并且可以被索引。
如果未指定关键字,则默认为VIRTUAL
。
在表中混合使用VIRTUAL
和STORED
列是允许的。
可以提供其他属性以指示列是否被索引或可以为NULL
,或提供注释。
生成列表达式必须遵守以下规则。如果表达式包含不允许的结构,则会发生错误。
-
文本,确定性内置函数和运算符是允许的。如果给定相同的表中数据,多次调用产生相同结果,则函数是确定性的,与连接的用户无关。不确定性并不符合此定义的函数示例:
CONNECTION_ID()
,CURRENT_USER()
,NOW()
。 -
不允许存储函数和可加载函数。
-
不允许存储过程和函数参数。
-
不允许变量(系统变量、用户定义变量和存储过程局部变量)。
-
不允许子查询。
-
生成列定义可以引用其他生成列,但只能引用表定义中较早出现的列。生成列定义可以引用表中的任何基本(非生成的)列,无论其定义是早于还是晚于。
-
AUTO_INCREMENT
属性不能在生成列定义中使用。 -
AUTO_INCREMENT
列不能作为生成列定义中的基本列使用。 -
如果表达式评估导致截断或向函数提供不正确的输入,则
CREATE TABLE
语句将以错误终止,并拒绝 DDL 操作。
如果表达式评估为与声明的列类型不同的数据类型,则根据通常的 MySQL 类型转换规则隐式强制转换为声明的类型。请参见第 14.3 节,“表达式评估中的类型转换”。
如果生成的列使用TIMESTAMP
数据类型,则explicit_defaults_for_timestamp
设置将被忽略。在这种情况下,如果此变量被禁用,则NULL
不会转换为CURRENT_TIMESTAMP
。在 MySQL 8.0.22 及更高版本中,如果列还声明为NOT NULL
,则尝试插入NULL
将明确拒绝,并显示ER_BAD_NULL_ERROR
。
注意
表达式评估使用评估时有效的 SQL 模式。如果表达式的任何组件依赖于 SQL 模式,则除非在所有使用期间 SQL 模式相同,否则可能会出现不同的结果。
对于CREATE TABLE ... LIKE
,目标表保留原始表的生成列信息。
对于CREATE TABLE ... SELECT
,目标表不保留所选自表中列是否为生成列的信息。语句的SELECT
部分不能为目标表中的生成列分配值。
允许通过生成列进行分区。请参见表分区。
存储生成列上的外键约束不能使用CASCADE
、SET NULL
或SET DEFAULT
作为ON UPDATE
参照操作,也不能使用SET NULL
或SET DEFAULT
作为ON DELETE
参照操作。
存储生成列的基列上的外键约束不能使用CASCADE
、SET NULL
或SET DEFAULT
作为ON UPDATE
或ON DELETE
引用动作。
外键约束不能引用虚拟生成列。
触发器不能使用NEW.*col_name*
或使用OLD.*col_name*
来引用生成列。
对于INSERT
、REPLACE
和UPDATE
,如果显式插入、替换或更新生成列,则唯一允许的值是DEFAULT
。
视图中的生成列被视为可更新,因为可以对其进行赋值。但是,如果显式更新此类列,则唯一允许的值是DEFAULT
。
生成列有几种用途,例如:
-
虚拟生成列可用作简化和统一查询的一种方式。可以将复杂条件定义为生成列,并从表上的多个查询中引用该条件,以确保它们都使用完全相同的条件。
-
存储生成列可用作复杂条件的物化缓存,这些条件在实时计算时成本高昂。
-
生成列可以模拟函数索引:使用生成列定义函数表达式并对其进行索引。这对于无法直接索引的类型列(例如
JSON
列)非常有用;请参见使用生成列创建 JSON 列索引,以获取详细示例。对于存储生成列,这种方法的缺点是值存储两次;一次作为生成列的值,一次作为索引中的值。
-
如果生成列被索引,优化器会识别与列定义匹配的查询表达式,并在查询执行期间适当地使用列的索引,即使查询没有直接按名称引用该列。有关详细信息,请参见第 10.3.11 节,“生成列索引的优化器使用”。
示例:
假设表t1
包含first_name
和last_name
列,并且应用程序经常使用类似以下表达式构建全名:
SELECT CONCAT(first_name,' ',last_name) AS full_name FROM t1;
避免编写表达式的一种方法是在t1
上创建视图v1
,这样可以通过直接选择full_name
来简化应用程序,而无需使用表达式:
CREATE VIEW v1 AS
SELECT *, CONCAT(first_name,' ',last_name) AS full_name FROM t1;
SELECT full_name FROM v1;
生成列还使应用程序能够直接选择full_name
,而无需定义视图:
CREATE TABLE t1 (
first_name VARCHAR(10),
last_name VARCHAR(10),
full_name VARCHAR(255) AS (CONCAT(first_name,' ',last_name))
);
SELECT full_name FROM t1;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?