MySQL8 中文参考(五十八)
原文:
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;
原文:
dev.mysql.com/doc/refman/8.0/en/create-table-secondary-indexes.html
15.1.20.9 二级索引和生成列
InnoDB支持虚拟生成列上的二级索引。不支持其他索引类型。在虚拟列上定义的二级索引有时被称为“虚拟索引”。
可以在一个或多个虚拟列上或在虚拟列和常规列或存储生成列的组合上创建二级索引。包含虚拟列的二级索引可以被定义为UNIQUE。
当在虚拟生成列上创建二级索引时,生成列的值会实现在索引记录中。如果索引是一个覆盖索引(包含查询检索的所有列),生成列的值将从索引结构中的实现值中检索,而不是实时计算。
使用虚拟列上的二级索引时需要考虑额外的写入成本,因为在INSERT和UPDATE操作期间,在二级索引记录中实现虚拟列值时执行计算。即使有额外的写入成本,虚拟列上的二级索引可能比生成存储列更可取,后者实现在聚簇索引中,导致需要更多磁盘空间和内存的更大表。如果在虚拟列上未定义二级索引,则读取时会有额外的成本,因为每次检查列的行时都必须计算虚拟列值。
虚拟列的索引列值被 MVCC 记录,以避免在回滚或清除操作期间重新计算生成列值。对于COMPACT和REDUNDANT行格式,记录值的数据长度受索引键限制的限制为 767 字节,对于DYNAMIC和COMPRESSED行格式,为 3072 字节。
在虚拟列上添加或删除二级索引是一个原地操作。
为提供 JSON 列索引而对生成列建立索引
如其他地方所述,JSON列不能直接建立索引。要创建引用此类列的索引,可以定义一个生成列,提取应该建立索引的信息,然后在生成列上创建索引,如下例所示:
mysql> CREATE TABLE jemp (
-> c JSON,
-> g INT GENERATED ALWAYS AS (c->"$.id"),
-> INDEX i (g)
-> );
Query OK, 0 rows affected (0.28 sec)
mysql> INSERT INTO jemp (c) VALUES
> ('{"id": "1", "name": "Fred"}'), ('{"id": "2", "name": "Wilma"}'),
> ('{"id": "3", "name": "Barney"}'), ('{"id": "4", "name": "Betty"}');
Query OK, 4 rows affected (0.04 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> SELECT c->>"$.name" AS name
> FROM jemp WHERE g > 2;
+--------+
| name |
+--------+
| Barney |
| Betty |
+--------+
2 rows in set (0.00 sec)
mysql> EXPLAIN SELECT c->>"$.name" AS name
> FROM jemp WHERE g > 2\G
*************************** 1\. row ***************************
id: 1
select_type: SIMPLE
table: jemp
partitions: NULL
type: range
possible_keys: i
key: i
key_len: 5
ref: NULL
rows: 2
filtered: 100.00
Extra: Using where 1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G
*************************** 1\. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name'))
AS `name` from `test`.`jemp` where (`test`.`jemp`.`g` > 2) 1 row in set (0.00 sec)
(我们已经将此示例中最后一条语句的输出包装起来以适应查看区域。)
当您在包含使用->或->>运算符的一个或多个表达式的SELECT或其他 SQL 语句上使用EXPLAIN时,这些表达式将被转换为使用JSON_EXTRACT()和(如果需要)JSON_UNQUOTE()的等效形式,如下所示,在EXPLAIN语句后立即显示的SHOW WARNINGS输出中:
mysql> EXPLAIN SELECT c->>"$.name"
> FROM jemp WHERE g > 2 ORDER BY c->"$.name"\G
*************************** 1\. row ***************************
id: 1
select_type: SIMPLE
table: jemp
partitions: NULL
type: range
possible_keys: i
key: i
key_len: 5
ref: NULL
rows: 2
filtered: 100.00
Extra: Using where; Using filesort 1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G
*************************** 1\. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name')) AS
`c->>"$.name"` from `test`.`jemp` where (`test`.`jemp`.`g` > 2) order by
json_extract(`test`.`jemp`.`c`,'$.name') 1 row in set (0.00 sec)
请参阅->和->>运算符的描述,以及JSON_EXTRACT()和JSON_UNQUOTE()函数的描述,获取更多信息和示例。
这种技术还可以用于提供间接引用其他类型列的索引,这些列不能直接进行索引,例如GEOMETRY列。
在 MySQL 8.0.21 及更高版本中,还可以使用JSON_VALUE()函数在JSON列上创建索引,使用可以优化查询的表达式。有关该函数的更多信息和示例,请参阅该函数的描述。
NDB Cluster 中的 JSON 列和间接索引
在 MySQL NDB Cluster 中,也可以使用 JSON 列的间接索引,但需要符合以下条件:
-
NDB将JSON列值在内部处理为BLOB。这意味着任何具有一个或多个 JSON 列的 NDB 表必须具有主键,否则无法记录在二进制日志中。 -
NDB存储引擎不支持虚拟列的索引。由于生成列的默认值是VIRTUAL,因此必须明确指定要应用间接索引的生成列为STORED。
用于创建此处显示的jempn表的**CREATE TABLE**语句是先前显示的jemp表的版本,经过修改以使其与NDB兼容:
CREATE TABLE jempn (
a BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
c JSON DEFAULT NULL,
g INT GENERATED ALWAYS AS (c->"$.id") STORED,
INDEX i (g)
) ENGINE=NDB;
我们可以使用以下INSERT语句填充这个表:
INSERT INTO jempn (c) VALUES
('{"id": "1", "name": "Fred"}'),
('{"id": "2", "name": "Wilma"}'),
('{"id": "3", "name": "Barney"}'),
('{"id": "4", "name": "Betty"}');
现在NDB可以使用索引i,如下所示:
mysql> EXPLAIN SELECT c->>"$.name" AS name
-> FROM jempn WHERE g > 2\G
*************************** 1\. row ***************************
id: 1
select_type: SIMPLE
table: jempn
partitions: p0,p1,p2,p3
type: range
possible_keys: i
key: i
key_len: 5
ref: NULL
rows: 3
filtered: 100.00
Extra: Using pushed condition (`test`.`jempn`.`g` > 2) 1 row in set, 1 warning (0.01 sec)
mysql> SHOW WARNINGS\G
*************************** 1\. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select
json_unquote(json_extract(`test`.`jempn`.`c`,'$.name')) AS `name` from
`test`.`jempn` where (`test`.`jempn`.`g` > 2) 1 row in set (0.00 sec)
你应该记住,存储的生成列以及该列上的任何索引都使用DataMemory。
15.1.20.10 不可见列
MySQL 从 MySQL 8.0.23 开始支持不可见列。不可见列通常对查询隐藏,但如果显式引用,则可以访问。在 MySQL 8.0.23 之前,所有列都是可见的。
作为不可见列可能有用的示例,假设一个应用程序使用SELECT *查询来访问表,并且必须继续工作而无需修改,即使表被修改以添加一个应用程序不希望存在的新列。在SELECT *查询中,*会计算所有表列,除了那些不可见的列,因此解决方案是将新列添加为不可见列。该列仍然对SELECT *查询“隐藏”,应用程序继续像以前一样工作。如果必要,新版本的应用程序可以通过显式引用来引用不可见列。
以下各节详细介绍了 MySQL 如何处理不可见列。
-
DDL 语句和不可见列
-
DML 语句和不可见列
-
不可见列元数据
-
二进制日志和不可见列
DDL 语句和不可见列
列默认为可见。要为新列明确指定可见性,请在CREATE TABLE或ALTER TABLE的列定义中使用VISIBLE或INVISIBLE关键字:
CREATE TABLE t1 (
i INT,
j DATE INVISIBLE
) ENGINE = InnoDB;
ALTER TABLE t1 ADD COLUMN k INT INVISIBLE;
要更改现有列的可见性,请在ALTER TABLE列修改子句中使用VISIBLE或INVISIBLE关键字之一:
ALTER TABLE t1 CHANGE COLUMN j j DATE VISIBLE;
ALTER TABLE t1 MODIFY COLUMN j DATE INVISIBLE;
ALTER TABLE t1 ALTER COLUMN j SET VISIBLE;
表必须至少有一个可见列。尝试使所有列不可见会产生错误。
不可见列支持通常的列属性:NULL、NOT NULL、AUTO_INCREMENT等。
生成列可以是不可见的。
索引定义可以命名不可见列,包括PRIMARY KEY和UNIQUE索引的定义。虽然表必须至少有一个可见列,但索引定义不需要有任何可见列。
从表中删除的不可见列会像通常一样从命名该列的任何索引定义中删除。
外键约束可以定义在不可见列上,并且外键约束可以引用不可见列。
CHECK约束可以定义在不可见列上。对于新的或修改的行,违反不可见列上的CHECK约束会产生错误。
CREATE TABLE ... LIKE 包含不可见列,并且在新表中也是不可见的。
CREATE TABLE ... SELECT 不包括不可见列,除非它们在SELECT部分中被明确引用。然而,即使被明确引用,现有表中不可见的列在新表中也是可见的:
mysql> CREATE TABLE t1 (col1 INT, col2 INT INVISIBLE);
mysql> CREATE TABLE t2 AS SELECT col1, col2 FROM t1;
mysql> SHOW CREATE TABLE t2\G
*************************** 1\. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`col1` int DEFAULT NULL,
`col2` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
如果要保留不可见性,请在CREATE TABLE部分的CREATE TABLE ... SELECT语句中为不可见列提供定义:
mysql> CREATE TABLE t1 (col1 INT, col2 INT INVISIBLE);
mysql> CREATE TABLE t2 (col2 INT INVISIBLE) AS SELECT col1, col2 FROM t1;
mysql> SHOW CREATE TABLE t2\G
*************************** 1\. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`col1` int DEFAULT NULL,
`col2` int DEFAULT NULL /*!80023 INVISIBLE */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
视图可以通过在定义视图的SELECT语句中明确引用它们来引用不可见列。在定义引用该列的视图之后更改列的可见性不会改变视图行为。
DML 语句和不可见列
对于SELECT语句,除非在选择列表中明确引用,否则不可见列不会成为结果集的一部分。在选择列表中,*和*tbl_name*.*的简写不包括不可见列。自然连接不包括不可见列。
考虑以下语句序列:
mysql> CREATE TABLE t1 (col1 INT, col2 INT INVISIBLE);
mysql> INSERT INTO t1 (col1, col2) VALUES(1, 2), (3, 4);
mysql> SELECT * FROM t1;
+------+
| col1 |
+------+
| 1 |
| 3 |
+------+
mysql> SELECT col1, col2 FROM t1;
+------+------+
| col1 | col2 |
+------+------+
| 1 | 2 |
| 3 | 4 |
+------+------+
第一个SELECT在选择列表中不引用不可见列col2(因为*不包括不可见列),所以col2不会出现在语句结果中。第二个SELECT明确引用col2,因此该列会出现在结果中。
语句TABLE t1 产生与第一个SELECT语句相同的输出。由于在TABLE语句中无法指定列,因此TABLE永远不会显示不可见列。
对于创建新行的语句,除非显式引用并赋值,否则不可见列将被分配其隐式默认值。有关隐式默认值的信息,请参阅隐式默认值处理。
对于INSERT(和REPLACE,对于未替换的行),当缺少列列表、空列列表或不包括不可见列的非空列列表时,隐式默认赋值会发生:
CREATE TABLE t1 (col1 INT, col2 INT INVISIBLE);
INSERT INTO t1 VALUES(...);
INSERT INTO t1 () VALUES(...);
INSERT INTO t1 (col1) VALUES(...);
对于前两个INSERT语句,VALUES()列表必须为每个可见列提供一个值,而不包括不可见列。对于第三个INSERT语句,VALUES()列表必须提供与命名列数相同的值;当您使用VALUES ROW()而不是VALUES()时也是如此。
对于LOAD DATA和LOAD XML,如果缺少列列表或非空列列表不包括不可见列,则会发生隐式默认赋值。输入行不应包含不可见列的值。
对于前述语句,如果要为不可见列分配除隐式默认值之外的值,请在列列表中明确命名不可见列并为其提供值。
INSERT INTO ... SELECT *和REPLACE INTO ... SELECT *不包括不可见列,因为*不包括不可见列。隐式默认赋值如前所述发生。
对于根据PRIMARY KEY或UNIQUE索引中的值插入或忽略新行,或替换或修改现有行的语句,MySQL 将不可见列视为可见列的相同方式处理:不可见列参与键值比较。具体来说,如果新行与唯一键值的现有行具有相同的值,则无论索引列是可见还是不可见,这些行为都会发生:
-
使用
IGNORE修饰符,INSERT、LOAD DATA和LOAD XML会忽略新行。 -
REPLACE用新行替换现有行。使用REPLACE修饰符,LOAD DATA和LOAD XML也是如此。 -
INSERT ... ON DUPLICATE KEY UPDATE更新现有行。
对于UPDATE语句更新不可见列,与可见列一样,需要命名并分配值。
不可见列元数据
列是否可见的信息可以从信息模式COLUMNS表的EXTRA列或SHOW COLUMNS输出中获取。例如:
mysql> SELECT TABLE_NAME, COLUMN_NAME, EXTRA
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 't1';
+------------+-------------+-----------+
| TABLE_NAME | COLUMN_NAME | EXTRA |
+------------+-------------+-----------+
| t1 | i | |
| t1 | j | |
| t1 | k | INVISIBLE |
+------------+-------------+-----------+
列默认可见,因此在这种情况下,EXTRA不显示可见性信息。对于不可见列,EXTRA显示INVISIBLE。
SHOW CREATE TABLE在表定义中显示不可见列,版本特定注释中包含INVISIBLE关键字:
mysql> SHOW CREATE TABLE t1\G
*************************** 1\. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`i` int DEFAULT NULL,
`j` int DEFAULT NULL,
`k` int DEFAULT NULL /*!80023 INVISIBLE */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
mysqldump和mysqlpump使用SHOW CREATE TABLE,因此它们在转储表定义时包括不可见列。它们还在转储数据时包括不可见列值。
将转储文件重新加载到不支持不可见列的旧版本的 MySQL 中会忽略特定于版本的注释,从而将任何不可见列创建为可见列。
二进制日志和不可见列
MySQL 在二进制日志中处理不可见列如下:
-
表创建事件为不可见列包括
INVISIBLE属性。 -
不可见列在行事件中被视为可见列。根据
binlog_row_image系统变量设置,如果需要,则包括它们。 -
应用行事件时,不可见列在行事件中被视为可见列。特别是,根据
slave_rows_search_algorithms系统变量设置选择要使用的算法和索引。 -
不可见列在计算写入集时被视为可见列。特别是,写入集包括在不可见列上定义的索引。
-
mysqlbinlog 命令在列元数据中包含可见性。
15.1.20.11 生成的隐式主键
从 MySQL 8.0.30 开始,MySQL 支持为没有显式主键的任何InnoDB表创建生成的隐式主键。当sql_generate_invisible_primary_key服务器系统变量设置为ON时,MySQL 服务器会自动向任何这样的表添加一个生成的隐式主键(GIPK)。
默认情况下,sql_generate_invisible_primary_key的值为OFF,这意味着禁用 GIPK 的自动添加。为了说明这如何影响表的创建,我们首先创建两个相同的表,都没有主键,唯一的区别是第一个(表auto_0)在创建时sql_generate_invisible_primary_key设置为OFF,而第二个(auto_1)在将其设置为ON后创建,如下所示:
mysql> SELECT @@sql_generate_invisible_primary_key;
+--------------------------------------+
| @@sql_generate_invisible_primary_key |
+--------------------------------------+
| 0 |
+--------------------------------------+
1 row in set (0.00 sec)
mysql> CREATE TABLE auto_0 (c1 VARCHAR(50), c2 INT);
Query OK, 0 rows affected (0.02 sec)
mysql> SET sql_generate_invisible_primary_key=ON;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@sql_generate_invisible_primary_key;
+--------------------------------------+
| @@sql_generate_invisible_primary_key |
+--------------------------------------+
| 1 |
+--------------------------------------+
1 row in set (0.00 sec)
mysql> CREATE TABLE auto_1 (c1 VARCHAR(50), c2 INT);
Query OK, 0 rows affected (0.04 sec)
比较这些SHOW CREATE TABLE语句的输出,看看表实际上是如何创建的:
mysql> SHOW CREATE TABLE auto_0\G
*************************** 1\. row ***************************
Table: auto_0
Create Table: CREATE TABLE `auto_0` (
`c1` varchar(50) DEFAULT NULL,
`c2` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
mysql> SHOW CREATE TABLE auto_1\G
*************************** 1\. row ***************************
Table: auto_1
Create Table: CREATE TABLE `auto_1` (
`my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */,
`c1` varchar(50) DEFAULT NULL,
`c2` int DEFAULT NULL,
PRIMARY KEY (`my_row_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
由于auto_1在创建时未指定主键,设置sql_generate_invisible_primary_key = ON会导致 MySQL 向该表添加不可见列my_row_id和在该列上的主键。由于在创建auto_0时sql_generate_invisible_primary_key为OFF,因此在该表上没有进行此类添加。
当服务器向表添加主键时,列和键名始终为my_row_id。因此,在以这种方式启用生成的隐式主键时,除非表创建语句还指定了显式主键,否则不能创建具有列名为my_row_id的表。(在这种情况下,您不需要在列或键中命名为my_row_id。)
my_row_id是一个不可见列,这意味着它不会显示在SELECT *或TABLE的输出中;必须通过名称显式选择该列。请参阅第 15.1.20.10 节,“不可见列”。
启用 GIPK 时,生成的主键除了在VISIBLE和INVISIBLE之间切换之外,不能进行其他更改。要使auto_1上的生成的隐式主键可见,请执行此ALTER TABLE语句:
mysql> ALTER TABLE auto_1 ALTER COLUMN my_row_id SET VISIBLE;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW CREATE TABLE auto_1\G
*************************** 1\. row ***************************
Table: auto_1
Create Table: CREATE TABLE `auto_1` (
`my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT,
`c1` varchar(50) DEFAULT NULL,
`c2` int DEFAULT NULL,
PRIMARY KEY (`my_row_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.01 sec)
要再次使此生成的主键不可见,请执行ALTER TABLE auto_1 ALTER COLUMN my_row_id SET INVISIBLE。
生成的隐式主键默认情况下始终是不可见的。
启用 GIPK 时,如果满足以下任一条件,则无法删除生成的主键:
-
表没有主键。
-
主键被删除,但主键列未被删除。
sql_generate_invisible_primary_key的影响仅适用于使用InnoDB存储引擎的表。您可以使用ALTER TABLE语句更改表使用的存储引擎,该表具有生成的不可见主键;在这种情况下,主键和列保持不变,但表和键不再接受任何特殊处理。
默认情况下,GIPK 会显示在SHOW CREATE TABLE、SHOW COLUMNS和SHOW INDEX的输出中,并且在信息模式的COLUMNS和STATISTICS表中可见。您可以通过将show_gipk_in_create_table_and_information_schema系统变量设置为OFF来隐藏这些情况下生成的不可见主键。默认情况下,此变量为ON,如下所示:
mysql> SELECT @@show_gipk_in_create_table_and_information_schema;
+----------------------------------------------------+
| @@show_gipk_in_create_table_and_information_schema |
+----------------------------------------------------+
| 1 |
+----------------------------------------------------+
1 row in set (0.00 sec)
如下查询COLUMNS表可见,my_row_id在auto_1的列中可见:
mysql> SELECT COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, COLUMN_KEY
-> FROM INFORMATION_SCHEMA.COLUMNS
-> WHERE TABLE_NAME = "auto_1";
+-------------+------------------+-----------+------------+
| COLUMN_NAME | ORDINAL_POSITION | DATA_TYPE | COLUMN_KEY |
+-------------+------------------+-----------+------------+
| my_row_id | 1 | bigint | PRI |
| c1 | 2 | varchar | |
| c2 | 3 | int | |
+-------------+------------------+-----------+------------+
3 rows in set (0.01 sec)
在将show_gipk_in_create_table_and_information_schema设置为OFF后,my_row_id不再在COLUMNS表中可见,如下所示:
mysql> SET show_gipk_in_create_table_and_information_schema = OFF;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@show_gipk_in_create_table_and_information_schema;
+----------------------------------------------------+
| @@show_gipk_in_create_table_and_information_schema |
+----------------------------------------------------+
| 0 |
+----------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, COLUMN_KEY
-> FROM INFORMATION_SCHEMA.COLUMNS
-> WHERE TABLE_NAME = "auto_1";
+-------------+------------------+-----------+------------+
| COLUMN_NAME | ORDINAL_POSITION | DATA_TYPE | COLUMN_KEY |
+-------------+------------------+-----------+------------+
| c1 | 2 | varchar | |
| c2 | 3 | int | |
+-------------+------------------+-----------+------------+
2 rows in set (0.00 sec)
sql_generate_invisible_primary_key的设置不会被复制,并且被复制的应用程序线程会忽略它。这意味着在源上设置此变量对副本没有影响。在 MySQL 8.0.32 及更高版本中,您可以通过在CHANGE REPLICATION SOURCE TO语句中使用REQUIRE_TABLE_PRIMARY_KEY_CHECK = GENERATE来使副本在给定复制通道上为没有主键的表添加 GIPK。
GIPK 与基于行的CREATE TABLE ... SELECT复制一起工作;在这种情况下,写入二进制日志的信息包括 GIPK 定义,因此正确复制。不支持sql_generate_invisible_primary_key = ON的基于语句的CREATE TABLE ... SELECT复制。
在创建或导入使用 GIPK 的安装备份时,可以排除生成的不可见主键列和值。--skip-generated-invisible-primary-key 选项用于 mysqldump,导致在程序输出中排除 GIPK 信息。如果您正在导入包含生成的不可见主键和值的转储文件,还可以使用 --skip-generated-invisible-primary-key 与 mysqlpump 一起使用,以使这些信息被抑制(因此不会被导入)。
原文:
dev.mysql.com/doc/refman/8.0/en/create-table-ndb-comment-options.html
15.1.20.12 设置 NDB 注释选项
-
NDB_COLUMN 选项
-
NDB_TABLE 选项
可以在 NDB 表的表注释或列注释中设置许多特定于 NDB Cluster 的选项。 通过使用 NDB_TABLE 在表注释中嵌入用于控制从任何副本读取和分区平衡的表级选项。
NDB_COLUMN 可以用于列注释,将 NDB 用于存储 blob 值的 blob 部分表列的大小设置为最大值。 这适用于 BLOB、MEDIUMBLOB、LONGBLOB、TEXT、MEDIUMTEXT、LONGTEXT 和 JSON 列。 从 NDB 8.0.30 开始,列注释还可以用于控制 blob 列的内联大小。 NDB_COLUMN 注释不支持 TINYBLOB 或 TINYTEXT 列,因为这些列具有固定大小的内联部分(仅)和无法存储在其他位置的单独部分。
NDB_TABLE 可以用于表注释,用于设置与分区平衡和表是否完全复制等相关的选项。
本节的其余部分描述了这些选项及其用法。
NDB_COLUMN 选项
在 NDB Cluster 中,CREATE TABLE 或 ALTER TABLE 语句中的列注释也可以用于指定 NDB_COLUMN 选项。从版本 8.0.30 开始,NDB 支持两个列注释选项 BLOB_INLINE_SIZE 和 MAX_BLOB_PART_SIZE。 (在 NDB 8.0.30 之前,仅支持 MAX_BLOB_PART_SIZE。)此选项的语法如下所示:
COMMENT 'NDB_COLUMN=*speclist*'
*speclist* := *spec*[,*spec*]
spec :=
BLOB_INLINE_SIZE=*value*
| MAX_BLOB_PART_SIZE[={0|1}]
BLOB_INLINE_SIZE 指定要由该列内联存储的字节数;其预期值为 1 - 29980 范围内的整数。 设置大于 29980 的值会引发错误; 允许设置小于 1 的值,但会导致使用列类型的默认内联大小。
您应该知道,此选项的最大值实际上是可以存储在一个 NDB 表的一行中的最大字节数; 行中的每列都会对此总数做出贡献。
您还应该记住,特别是在处理 TEXT 列时,由 MAX_BLOB_PART_SIZE 或 BLOB_INLINE_SIZE 设置的值表示列大小(以字节为单位)。 它不表示字符数,字符数根据列使用的字符集和排序规则而变化。
要查看此选项的效果,请首先创建一个具有两个BLOB列的表,一个(b1)没有额外选项,另一个(b2)设置了BLOB_INLINE_SIZE,如下所示:
mysql> CREATE TABLE t1 (
-> a INT NOT NULL PRIMARY KEY,
-> b1 BLOB,
-> b2 BLOB COMMENT 'NDB_COLUMN=BLOB_INLINE_SIZE=8000'
-> ) ENGINE NDB;
Query OK, 0 rows affected (0.32 sec)
您可以通过查询ndbinfo.blobs表来查看BLOB列的BLOB_INLINE_SIZE设置,如下所示:
mysql> SELECT
-> column_name AS 'Column Name',
-> inline_size AS 'Inline Size',
-> part_size AS 'Blob Part Size'
-> FROM ndbinfo.blobs
-> WHERE table_name = 't1';
+-------------+-------------+----------------+
| Column Name | Inline Size | Blob Part Size |
+-------------+-------------+----------------+
| b1 | 256 | 2000 |
| b2 | 8000 | 2000 |
+-------------+-------------+----------------+
2 rows in set (0.01 sec)
您还可以检查ndb_desc实用程序的输出,如下所示,相关行以强调文本显示:
$> ndb_desc -d test t1
-- t --
Version: 1
Fragment type: HashMapPartition
K Value: 6
Min load factor: 78
Max load factor: 80
Temporary table: no
Number of attributes: 3
Number of primary keys: 1
Length of frm data: 945
Max Rows: 0
Row Checksum: 1
Row GCI: 1
SingleUserMode: 0
ForceVarPart: 1
PartitionCount: 2
FragmentCount: 2
PartitionBalance: FOR_RP_BY_LDM
ExtraRowGciBits: 0
ExtraRowAuthorBits: 0
TableStatus: Retrieved
Table options: readbackup
HashMap: DEFAULT-HASHMAP-3840-2
-- Attributes --
a Int PRIMARY KEY DISTRIBUTION KEY AT=FIXED ST=MEMORY
*b1 Blob(256,2000,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_64_1
b2 Blob(8000,2000,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_64_2* -- Indexes --
PRIMARY KEY(a) - UniqueHashIndex
PRIMARY(a) - OrderedIndex
对于MAX_BLOB_PART_SIZE,=号和其后的值是可选的。使用除 0 或 1 之外的任何值会导致语法错误。
在列注释中使用MAX_BLOB_PART_SIZE的效果是将TEXT或BLOB列的 blob 部分大小设置为NDB支持的最大字节数(13948)。此选项可应用于 MySQL 支持的除TINYBLOB或TINYTEXT之外的任何 blob 列类型(BLOB、MEDIUMBLOB、LONGBLOB、TEXT、MEDIUMTEXT、LONGTEXT)。与BLOB_INLINE_SIZE不同,MAX_BLOB_PART_SIZE对JSON列没有影响。
要查看此选项的效果,我们首先在mysql客户端中运行以下 SQL 语句,创建一个具有两个BLOB列的表,一个(c1)没有额外选项,另一个(c2)具有MAX_BLOB_PART_SIZE:
mysql> CREATE TABLE test.t2 (
-> p INT PRIMARY KEY,
-> c1 BLOB,
-> c2 BLOB COMMENT 'NDB_COLUMN=MAX_BLOB_PART_SIZE'
-> ) ENGINE NDB;
Query OK, 0 rows affected (0.32 sec)
从系统 shell 中运行ndb_desc实用程序,以获取有关刚刚创建的表的信息,如此示例所示:
$> ndb_desc -d test t2
-- t --
Version: 1
Fragment type: HashMapPartition
K Value: 6
Min load factor: 78
Max load factor: 80
Temporary table: no
Number of attributes: 3
Number of primary keys: 1
Length of frm data: 324
Row Checksum: 1
Row GCI: 1
SingleUserMode: 0
ForceVarPart: 1
FragmentCount: 2
ExtraRowGciBits: 0
ExtraRowAuthorBits: 0
TableStatus: Retrieved
HashMap: DEFAULT-HASHMAP-3840-2
-- Attributes --
p Int PRIMARY KEY DISTRIBUTION KEY AT=FIXED ST=MEMORY
*c1 Blob(256,2000,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_22_1
c2 Blob(256,13948,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_22_2* -- Indexes --
PRIMARY KEY(p) - UniqueHashIndex
PRIMARY(p) - OrderedIndex
输出中的列信息在Attributes下列出;对于c1和c2列,它在这里以强调文本显示。对于c1,blob 部分大小为 2000,即默认值;对于c2,它为 13948,由MAX_BLOB_PART_SIZE设置。
您还可以查询ndbinfo.blobs表来查看此内容,如下所示:
mysql> SELECT
-> column_name AS 'Column Name',
-> inline_size AS 'Inline Size',
-> part_size AS 'Blob Part Size'
-> FROM ndbinfo.blobs
-> WHERE table_name = 't2';
+-------------+-------------+----------------+
| Column Name | Inline Size | Blob Part Size |
+-------------+-------------+----------------+
| c1 | 256 | 2000 |
| c2 | 256 | 13948 |
+-------------+-------------+----------------+
2 rows in set (0.00 sec)
您可以使用类似于这样的ALTER TABLE语句更改NDB表的给定 blob 列的 blob 部分大小,并使用SHOW CREATE TABLE在之后验证更改:
mysql> ALTER TABLE test.t2
-> DROP COLUMN c1,
-> ADD COLUMN c1 BLOB COMMENT 'NDB_COLUMN=MAX_BLOB_PART_SIZE',
-> CHANGE COLUMN c2 c2 BLOB AFTER c1;
Query OK, 0 rows affected (0.47 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SHOW CREATE TABLE test.t2\G
*************************** 1\. row ***************************
Table: t
Create Table: CREATE TABLE `t2` (
`p` int(11) NOT NULL,
`c1` blob COMMENT 'NDB_COLUMN=MAX_BLOB_PART_SIZE',
`c2` blob,
PRIMARY KEY (`p`)
) ENGINE=ndbcluster DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
mysql> EXIT
Bye
ndb_desc的输出显示列的 blob 部分大小已按预期更改:
$> ndb_desc -d test t2
-- t --
Version: 16777220
Fragment type: HashMapPartition
K Value: 6
Min load factor: 78
Max load factor: 80
Temporary table: no
Number of attributes: 3
Number of primary keys: 1
Length of frm data: 324
Row Checksum: 1
Row GCI: 1
SingleUserMode: 0
ForceVarPart: 1
FragmentCount: 2
ExtraRowGciBits: 0
ExtraRowAuthorBits: 0
TableStatus: Retrieved
HashMap: DEFAULT-HASHMAP-3840-2
-- Attributes --
p Int PRIMARY KEY DISTRIBUTION KEY AT=FIXED ST=MEMORY
*c1 Blob(256,13948,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_26_1
c2 Blob(256,2000,0) NULL AT=MEDIUM_VAR ST=MEMORY BV=2 BT=NDB$BLOB_26_2* -- Indexes --
PRIMARY KEY(p) - UniqueHashIndex
PRIMARY(p) - OrderedIndex
您还可以通过再次运行针对ndbinfo.blobs的查询来查看更改:
mysql> SELECT
-> column_name AS 'Column Name',
-> inline_size AS 'Inline Size',
-> part_size AS 'Blob Part Size'
-> FROM ndbinfo.blobs
-> WHERE table_name = 't2';
+-------------+-------------+----------------+
| Column Name | Inline Size | Blob Part Size |
+-------------+-------------+----------------+
| c1 | 256 | 13948 |
| c2 | 256 | 2000 |
+-------------+-------------+----------------+
2 rows in set (0.00 sec)
可以为 blob 列同时设置BLOB_INLINE_SIZE和MAX_BLOB_PART_SIZE,如此CREATE TABLE语句所示:
mysql> CREATE TABLE test.t3 (
-> p INT NOT NULL PRIMARY KEY,
-> c1 JSON,
-> c2 JSON COMMENT 'NDB_COLUMN=BLOB_INLINE_SIZE=5000,MAX_BLOB_PART_SIZE'
-> ) ENGINE NDB;
Query OK, 0 rows affected (0.28 sec)
查询blobs表显示该语句按预期工作:
mysql> SELECT
-> column_name AS 'Column Name',
-> inline_size AS 'Inline Size',
-> part_size AS 'Blob Part Size'
-> FROM ndbinfo.blobs
-> WHERE table_name = 't3';
+-------------+-------------+----------------+
| Column Name | Inline Size | Blob Part Size |
+-------------+-------------+----------------+
| c1 | 4000 | 8100 |
| c2 | 5000 | 8100 |
+-------------+-------------+----------------+
2 rows in set (0.00 sec)
您还可以通过检查 ndb_desc 的输出来验证该语句是否有效。
更改列的 blob 部分大小必须使用复制的 ALTER TABLE 来完成;此操作无法在线执行(参见 第 25.6.12 节,“NDB 集群中的 ALTER TABLE 在线操作”)。
有关 NDB 如何存储 blob 类型列的更多信息,请参阅 字符串类型存储要求。
NDB_TABLE 选项
对于 NDB 集群表,在 CREATE TABLE 或 ALTER TABLE 语句中的表注释也可以用于指定一个 NDB_TABLE 选项,该选项由一个或多个名称-值对组成,如果需要,用逗号分隔,跟在字符串 NDB_TABLE= 后面。名称和值的完整语法如下所示:
COMMENT="NDB_TABLE=*ndb_table_option*[,*ndb_table_option*[,...]]"
*ndb_table_option*: {
NOLOGGING={1 | 0}
| READ_BACKUP={1 | 0}
| PARTITION_BALANCE={FOR_RP_BY_NODE | FOR_RA_BY_NODE | FOR_RP_BY_LDM
| FOR_RA_BY_LDM | FOR_RA_BY_LDM_X_2
| FOR_RA_BY_LDM_X_3 | FOR_RA_BY_LDM_X_4}
| FULLY_REPLICATED={1 | 0}
}
引号字符串内不允许有空格。字符串不区分大小写。
可以通过表注释中的四个 NDB 表选项来设置这种方式。这几个选项将在接下来的几段中详细描述。
NOLOGGING:默认情况下,NDB 表是记录并进行检查点的。这使得它们在整个集群失败时是持久的。在创建或更改表时使用 NOLOGGING 意味着该表不会被重做日志记录或包含在本地检查点中。在这种情况下,该表仍然在数据节点之间复制以实现高可用性,并使用事务进行更新,但对其所做的更改不会记录在数据节点的重做日志中,并且其内容不会被检查点到磁盘上;在从集群故障中恢复时,集群会保留表定义,但不保留任何行,也就是说,该表是空的。
使用这种非记录表减少了数据节点对磁盘 I/O 和存储的需求,以及用于检查点的 CPU。这可能适用于频繁更新的短期数据,并且在极少数情况下发生总集群故障时可以接受所有数据的丢失。
还可以使用 ndb_table_no_logging 系统变量,使得在此变量生效时创建或更改的任何 NDB 表都表现得好像已经使用 NOLOGGING 注释创建。与直接使用注释时不同,在 SHOW CREATE TABLE 的输出中没有任何内容表明它是一个非记录表。推荐使用表注释方法,因为它提供了对该功能的每个表的控制,并且表模式的这一方面嵌入在表创建语句中,可以很容易地被 SQL 工具找到。
READ_BACKUP: 将此选项设置为 1 的效果与启用ndb_read_backup相同;允许从任何副本读取。这样做极大地提高了从表中读取的性能,对写入性能的影响相对较小。从 NDB 8.0.19 开始,READ_BACKUP的默认值为 1,ndb_read_backup的默认值为ON(以前,默认情况下,从任何副本读取是禁用的)。
您可以在线为现有表设置READ_BACKUP,使用类似于以下示例的ALTER TABLE语句:
ALTER TABLE ... ALGORITHM=INPLACE, COMMENT="NDB_TABLE=READ_BACKUP=1";
ALTER TABLE ... ALGORITHM=INPLACE, COMMENT="NDB_TABLE=READ_BACKUP=0";
有关ALTER TABLE的ALGORITHM选项的更多信息,请参阅第 25.6.12 节,“NDB Cluster 中的 ALTER TABLE 在线操作”。
PARTITION_BALANCE: 提供对分区分配和放置的额外控制。支持以下四种方案:
-
FOR_RP_BY_NODE: 每个节点一个分区。每个节点上只有一个 LDM 存储主分区。每个分区在所有节点上的相同 LDM(相同 ID)中存储。
-
FOR_RA_BY_NODE: 每个节点组一个分区。每个节点存储一个分区,可以是主复制品或备份复制品。每个分区在所有节点上都存储在相同的 LDM 中。
-
FOR_RP_BY_LDM: 每个节点上每个 LDM 一个分区;默认设置。如果将
READ_BACKUP设置为 1,则使用此设置。 -
FOR_RA_BY_LDM: 每个节点组中每个 LDM 一个分区。这些分区可以是主分区或备份分区。
-
FOR_RA_BY_LDM_X_2: 每个节点组中每个 LDM 有两个分区。这些分区可以是主分区或备份分区。
-
FOR_RA_BY_LDM_X_3: 每个节点组中每个 LDM 有三个分区。这些分区可以是主分区或备份分区。
-
FOR_RA_BY_LDM_X_4: 每个节点组中每个 LDM 有四个分区。这些分区可以是主分区或备份分区。
PARTITION_BALANCE是设置每个表分区数的首选接口。使用MAX_ROWS强制分区数已被弃用,但仍继续支持以确保向后兼容性;它可能在将来的 MySQL NDB Cluster 版本中被移除(Bug #81759,Bug #23544301)。
FULLY_REPLICATED控制表是否完全复制,即每个数据节点是否有表的完整副本。要启用表的完全复制,使用FULLY_REPLICATED=1。
这个设置也可以使用ndb_fully_replicated系统变量来控制。将其设置为ON会默认为所有新的NDB表启用该选项;默认值为OFF。ndb_data_node_neighbour系统变量也用于完全复制的表,以确保访问完全复制的表时,我们访问与此 MySQL 服务器本地的数据节点。
下面显示了创建NDB表时使用此类注释的CREATE TABLE语句示例:
mysql> 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表来获取,例如:
mysql> SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_COMMENT
> FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME="t1"\G
*************************** 1\. row ***************************
TABLE_NAME: t1
TABLE_SCHEMA: test
TABLE_COMMENT: NDB_TABLE=READ_BACKUP=0,PARTITION_BALANCE=FOR_RP_BY_NODE 1 row in set (0.01 sec)
此注释语法也适用于NDB表的ALTER TABLE语句,如下所示:
mysql> ALTER TABLE t1 COMMENT="NDB_TABLE=PARTITION_BALANCE=FOR_RA_BY_NODE";
Query OK, 0 rows affected (0.40 sec)
Records: 0 Duplicates: 0 Warnings: 0
从 NDB 8.0.21 开始,TABLE_COMMENT列显示了在ALTER TABLE语句后重新创建表所需的注释,如下所示:
mysql> SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_COMMENT
-> FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME="t1"\G
*************************** 1\. row ***************************
TABLE_NAME: t1
TABLE_SCHEMA: test
TABLE_COMMENT: NDB_TABLE=READ_BACKUP=0,PARTITION_BALANCE=FOR_RP_BY_NODE 1 row in set (0.01 sec)
mysql> SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_COMMENT
> FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME="t1";
+------------+--------------+--------------------------------------------------+
| TABLE_NAME | TABLE_SCHEMA | TABLE_COMMENT |
+------------+--------------+--------------------------------------------------+
| t1 | c | NDB_TABLE=PARTITION_BALANCE=FOR_RA_BY_NODE |
| t1 | d | |
+------------+--------------+--------------------------------------------------+
2 rows in set (0.01 sec)
请记住,与ALTER TABLE一起使用的表注释会替换表可能已有的任何现有注释。
mysql> ALTER TABLE t1 COMMENT="NDB_TABLE=PARTITION_BALANCE=FOR_RA_BY_NODE";
Query OK, 0 rows affected (0.40 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_COMMENT
> FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME="t1";
+------------+--------------+--------------------------------------------------+
| TABLE_NAME | TABLE_SCHEMA | TABLE_COMMENT |
+------------+--------------+--------------------------------------------------+
| t1 | c | NDB_TABLE=PARTITION_BALANCE=FOR_RA_BY_NODE |
| t1 | d | |
+------------+--------------+--------------------------------------------------+
2 rows in set (0.01 sec)
在 NDB 8.0.21 之前,与ALTER TABLE一起使用的表注释会替换表可能已有的任何现有注释。这意味着(例如)READ_BACKUP值不会传递到由ALTER TABLE语句设置的新注释中,并且任何未指定的值都会恢复为默认值。(BUG#30428829)因此,使用 SQL 不再有任何方法来检索先前为注释设置的值。为了防止注释值恢复为默认值,需要保留现有注释字符串中的任何此类值,并将它们包含在传递给ALTER TABLE的注释中。
在ndb_desc的输出中,您还可以看到PARTITION_BALANCE选项的价值。ndb_desc还显示了表中是否设置了READ_BACKUP和FULLY_REPLICATED选项。有关此程序的更多信息,请参阅其描述。
15.1.21 创建表空间语句
CREATE [UNDO] TABLESPACE *tablespace_name*
*InnoDB and NDB:*
[ADD DATAFILE '*file_name*']
[AUTOEXTEND_SIZE [=] *value*]
*InnoDB only:*
[FILE_BLOCK_SIZE = value]
[ENCRYPTION [=] {'Y' | 'N'}]
*NDB only:*
USE LOGFILE GROUP *logfile_group*
[EXTENT_SIZE [=] *extent_size*]
[INITIAL_SIZE [=] *initial_size*]
[MAX_SIZE [=] *max_size*]
[NODEGROUP [=] *nodegroup_id*]
[WAIT]
[COMMENT [=] '*string*']
*InnoDB and NDB:*
[ENGINE [=] *engine_name*]
*Reserved for future use:*
[ENGINE_ATTRIBUTE [=] '*string*']
此语句用于创建一个表空间。精确的语法和语义取决于所使用的存储引擎。在标准 MySQL 版本中,这始终是一个InnoDB表空间。MySQL NDB Cluster 还支持使用NDB存储引擎的表空间。
-
InnoDB 的考虑事项
-
NDB Cluster 的考虑事项
-
选项
-
注意事项
-
InnoDB 示例
-
NDB 示例
InnoDB 的考虑事项
CREATE TABLESPACE语法用于创建通用表空间或撤销表空间。在 MySQL 8.0.14 中引入的UNDO关键字必须指定以创建撤销表空间。
通用表空间是一个共享表空间。它可以容纳多个表,并支持所有表行格式。通用表空间可以相对于数据目录或独立于数据目录创建。
创建InnoDB通用表空间后,使用CREATE TABLE *tbl_name* ... TABLESPACE [=] *tablespace_name*或ALTER TABLE *tbl_name* TABLESPACE [=] *tablespace_name*来将表添加到表空间中。更多信息,请参见第 17.6.3.3 节,“通用表空间”。
撤销表空间包含撤销日志。可以通过指定完全限定的数据文件路径在所选位置创建撤销表空间。更多信息,请参见第 17.6.3.4 节,“撤销表空间”。
NDB Cluster 的考虑事项
此语句用于创建一个表空间,可以包含一个或多个数据文件,为 NDB Cluster Disk Data 表提供存储空间(参见第 25.6.11 节,“NDB Cluster Disk Data Tables”)。使用此语句创建一个数据文件并将其添加到表空间中。可以使用ALTER TABLESPACE语句(参见第 15.1.10 节,“ALTER TABLESPACE Statement”)向表空间添加其他数据文件。
注意
所有 NDB 集群磁盘数据对象共享相同的命名空间。这意味着每个磁盘数据对象必须具有唯一的名称(而不仅仅是给定类型的每个磁盘数据对象)。例如,您不能拥有具有相同名称的表空间和日志文件组,或者具有相同名称的表空间和数据文件。
一个或多个UNDO日志文件的日志文件组必须分配给要使用USE LOGFILE GROUP子句创建的表空间。*logfile_group*必须是使用CREATE LOGFILE GROUP创建的现有日志文件组(参见 Section 15.1.16, “CREATE LOGFILE GROUP Statement”)。多个表空间可以使用相同的日志文件组进行UNDO日志记录。
在设置EXTENT_SIZE或INITIAL_SIZE时,您可以选择在数字后面跟随一个数量级的单字母缩写,类似于my.cnf中使用的缩写。通常,这是M(表示兆字节)或G(表示千兆字节)中的一个字母。
INITIAL_SIZE和EXTENT_SIZE将按以下方式进行四舍五入:
-
EXTENT_SIZE四舍五入到最接近的 32K 的整数倍。 -
INITIAL_SIZE向下四舍五入到最接近的 32K 的整数倍;此结果向上四舍五入到最接近EXTENT_SIZE的整数倍(在任何四舍五入之后)。
注意
NDB为数据节点重新启动操作保留了表空间的 4%。此保留空间不能用于数据存储。
描述的四舍五入是显式完成的,并且当 MySQL 服务器执行任何此类四舍五入时会发出警告。这些四舍五入的值也被 NDB 内核用于计算INFORMATION_SCHEMA.FILES列值和其他用途。然而,为了避免意外结果,我们建议您在指定这些选项时始终使用 32K 的整数倍。
当使用ENGINE [=] NDB进行CREATE TABLESPACE时,在每个集群数据节点上创建一个表空间和关联的数据文件。您可以通过查询信息模式FILES表来验证数据文件是否已创建并获取有关它们的信息。(请参见本节后面的示例。)
(请参见 Section 28.3.15, “The INFORMATION_SCHEMA FILES Table”。)
选项
-
ADD DATAFILE:定义表空间数据文件的名称。在创建NDB表空间时,此选项始终是必需的;对于 MySQL 8.0.14 及更高版本的InnoDB,仅在创建撤销表空间时才是必需的。*file_name*,包括任何指定的路径,必须用单引号或双引号括起来。文件名(不包括文件扩展名)和目录名必须至少为一个字节的长度。不支持零长度的文件名和目录名。由于
InnoDB和NDB在处理数据文件方面存在相当大的差异,因此在接下来的讨论中,这两种存储引擎将分别进行讨论。InnoDB 数据文件。 一个
InnoDB表空间仅支持单个数据文件,其名称必须包含.ibd扩展名。要将
InnoDB通用表空间数据文件放在数据目录之外的位置,包括完全限定的路径或相对于数据目录的路径。仅允许为撤销表空间指定完全限定路径。如果不指定路径,则通用表空间将在数据目录中创建。未指定路径创建的撤销表空间将在由innodb_undo_directory变量定义的目录中创建。如果未定义innodb_undo_directory变量,则撤销表空间将在数据目录中创建。为避免与隐式创建的按表创建文件表空间发生冲突,不支持在数据目录的子目录下创建
InnoDB通用表空间。在数据目录之外创建通用表空间或撤销表空间时,目录必须存在,并且在创建表空间之前必须为InnoDB所知。要使目录为InnoDB所知,将其添加到innodb_directories值或将其添加到其值附加到innodb_directories值的变量之一。innodb_directories是一个只读变量。配置它需要重新启动服务器。如果在创建
InnoDB表空间时未指定ADD DATAFILE子句,则将隐式创建具有唯一文件名的表空间数据文件。唯一文件名是一个 128 位 UUID,格式为由短横线分隔的五组十六进制数字。如果存储引擎需要,将添加文件扩展名。对于InnoDB通用表空间数据文件,将添加.ibd文件扩展名。在复制环境中,在复制源服务器上创建的数据文件名与在副本上创建的数据文件名不同。截至 MySQL 8.0.17,
ADD DATAFILE子句在创建InnoDB表空间时不允许循环目录引用。例如,以下语句中的循环目录引用(/../)是不允许的:CREATE TABLESPACE ts1 ADD DATAFILE ts1.ibd '*any_directory*/../ts1.ibd';在 Linux 上存在一个例外,如果前面的目录是一个符号链接,则允许循环目录引用。例如,如果*
any_directory*是一个符号链接,则上面示例中的数据文件路径是允许的。(数据文件路径仍然可以以'../'开头。)NDB 数据文件。 一个
NDB表空间支持多个数据文件,这些文件可以具有任何合法的文件名;可以使用ALTER TABLESPACE语句在创建后向 NDB 集群表空间添加更多数据文件。默认情况下,
NDB表空间数据文件会在数据节点文件系统目录中创建,即在数据节点的数据目录(DataDir)下名为ndb_*nodeid*_fs/TS的目录中,其中*nodeid*是数据节点的NodeId。要将数据文件放在除默认位置之外的位置,请包含绝对目录路径或相对于默认位置的路径。如果指定的目录不存在,NDB会尝试创建它;数据节点进程正在运行的系统用户帐户必须具有适当的权限才能这样做。注意
在确定数据文件路径时,
NDB不会展开~(波浪号)字符。当在同一物理主机上运行多个数据节点时,以下注意事项适用:
-
创建数据文件时不能指定绝对路径。
-
除非每个数据节点都有单独的数据目录,否则无法在数据节点文件系统目录之外创建表空间数据文件。
-
如果每个数据节点都有自己的数据目录,则数据文件可以在该目录的任何位置创建。
-
如果每个数据节点都有自己的数据目录,那么也可以通过相对路径在节点的数据目录之外创建数据文件,只要这个路径对于在主机文件系统上运行的每个数据节点来说都解析为唯一位置。
-
-
FILE_BLOCK_SIZE:此选项专门针对InnoDB通用表空间,对于NDB是被忽略的,它定义了表空间数据文件的块大小。值可以用字节或千字节来指定。例如,可以将 8 千字节的文件块大小指定为 8192 或 8K。如果不指定此选项,FILE_BLOCK_SIZE默认为innodb_page_size的值。当您打算将表空间用于存储压缩的InnoDB表(ROW_FORMAT=COMPRESSED)时,必须定义表空间的FILE_BLOCK_SIZE。如果
FILE_BLOCK_SIZE等于innodb_page_size值,则表空间只能包含具有未压缩行格式(COMPACT,REDUNDANT和DYNAMIC)的表。具有COMPRESSED行格式的表具有与未压缩表不同的物理页大小。因此,压缩表不能与未压缩表共存于同一表空间中。对于包含压缩表的通用表空间,必须指定
FILE_BLOCK_SIZE,并且FILE_BLOCK_SIZE的值必须是与innodb_page_size值相关的有效压缩页大小。此外,压缩表的物理页大小(KEY_BLOCK_SIZE)必须等于FILE_BLOCK_SIZE/1024。例如,如果innodb_page_size=16K,而FILE_BLOCK_SIZE=8K,则表的KEY_BLOCK_SIZE必须为 8。更多信息,请参见 Section 17.6.3.3, “通用表空间”。 -
USE LOGFILE GROUP:对于NDB必需,这是先前使用CREATE LOGFILE GROUP创建的日志文件组的名称。对于InnoDB不支持,会导致错误。 -
EXTENT_SIZE:此选项特定于 NDB,在 InnoDB 中不受支持,会导致错误。EXTENT_SIZE设置表空间中任何文件使用的 extent 的大小(以字节为单位)。默认值为 1M。最小大小为 32K,理论上的最大值为 2G,尽管实际最大大小取决于许多因素。在大多数情况下,更改 extent 大小对性能没有任何可测量的影响,建议除了最不寻常的情况外,使用默认值。一个 extent 是磁盘空间分配的单位。一个 extent 填满尽可能多的数据,然后使用另一个 extent。理论上,每个数据文件最多可以使用 65,535(64K)个 extent;然而,建议的最大值为 32,768(32K)。单个数据文件的建议最大大小为 32G,即 32K extents × 每个 extent 1MB。此外,一旦将 extent 分配给给定分区,就不能用于存储来自不同分区的数据;一个 extent 不能存储来自多个分区的数据。这意味着,例如,一个具有单个数据文件的表空间,其
INITIAL_SIZE(在下一项中描述)为 256 MB,EXTENT_SIZE为 128M,只有两个 extent,因此最多可以用于存储来自最多两个不同磁盘数据表分区的数据。通过查询信息模式
FILES表,您可以看到给定数据文件中剩余的空间有多少,从而估算文件中剩余的空间量。有关进一步讨论和示例,请参见 Section 28.3.15, “The INFORMATION_SCHEMA FILES Table”。 -
INITIAL_SIZE:此选项特定于NDB,不受InnoDB支持,在那里会出现错误。INITIAL_SIZE参数设置了使用ADD DATATFILE指定的数据文件的总大小(以字节为单位)。创建了此文件后,其大小不能更改;但是,您可以使用ALTER TABLESPACE ... ADD DATAFILE向表空间添加更多数据文件。INITIAL_SIZE是可选的;其默认值为 134217728(128 MB)。在 32 位系统上,
INITIAL_SIZE的最大支持值为 4294967296(4 GB)。 -
AUTOEXTEND_SIZE:在 MySQL 8.0.23 之前被 MySQL 忽略;从 MySQL 8.0.23 开始,定义了InnoDB在表空间变满时扩展的量。设置必须是 4MB 的倍数。默认设置为 0,这将导致表空间根据隐式默认行为进行扩展。有关更多信息,请参见 Section 17.6.3.9, “Tablespace AUTOEXTEND_SIZE Configuration”。在任何 MySQL NDB Cluster 8.0 的版本中都没有任何影响,无论使用的存储引擎是什么。
-
MAX_SIZE:当前 MySQL 忽略;保留以备可能的将来使用。在 MySQL 8.0 或 MySQL NDB Cluster 8.0 的任何版本中都没有任何影响,无论使用的存储引擎是什么。 -
NODEGROUP:当前 MySQL 忽略;保留以备可能的将来使用。在 MySQL 8.0 或 MySQL NDB Cluster 8.0 的任何版本中都没有任何影响,无论使用的存储引擎是什么。 -
WAIT:当前 MySQL 忽略;保留以备可能的将来使用。在 MySQL 8.0 或 MySQL NDB Cluster 8.0 的任何版本中都没有任何影响,无论使用的存储引擎是什么。 -
COMMENT:当前 MySQL 忽略;保留以备可能的将来使用。在 MySQL 8.0 或 MySQL NDB Cluster 8.0 的任何版本中都没有任何影响,无论使用的存储引擎是什么。 -
ENCRYPTION子句为InnoDB通用表空间启用或禁用页面级数据加密。MySQL 8.0.13 引入了对通用表空间的加密支持。截至 MySQL 8.0.16,如果未指定
ENCRYPTION子句,则default_table_encryption设置控制是否启用加密。ENCRYPTION子句会覆盖default_table_encryption设置。但是,如果启用了table_encryption_privilege_check变量,则需要TABLE_ENCRYPTION_ADMIN权限才能使用与default_table_encryption设置不同的ENCRYPTION子句设置。必须在创建启用加密的表空间之前安装和配置一个密钥环插件。
当通用表空间加密时,驻留在表空间中的所有表都会被加密。同样,创建在加密表空间中的表也会被加密。
更多信息,请参见 第 17.13 节,“InnoDB 数据静态加密”
-
ENGINE:定义使用表空间的存储引擎,其中engine_name是存储引擎的名称。目前,标准 MySQL 8.0 发行版仅支持InnoDB存储引擎。MySQL NDB Cluster 支持NDB和InnoDB表空间。如果未指定选项,则ENGINE使用default_storage_engine系统变量的值。 -
ENGINE_ATTRIBUTE选项(自 MySQL 8.0.21 起可用)用于指定主存储引擎的表空间属性。该选项保留供将来使用。允许的值是包含有效
JSON文档的字符串文字或空字符串('')。无效的JSON会被拒绝。CREATE TABLESPACE ts1 ENGINE_ATTRIBUTE='{"*key*":"*value*"}';ENGINE_ATTRIBUTE值可以重复而不会出错。在这种情况下,将使用最后指定的值。ENGINE_ATTRIBUTE值不会被服务器检查,也不会在表的存储引擎更改时清除。
注意
-
有关 MySQL 表空间命名规则,请参见 第 11.2 节,“模式对象名称”。除了这些规则外,斜杠字符(“/”)不允许使用,也不能使用以
innodb_开头的名称,因为此前缀保留供系统使用。 -
创建临时通用表空间不受支持。
-
通用表空间不支持临时表。
-
TABLESPACE选项可用于将InnoDB表分区或子分区分配给文件-每表表空间,可与CREATE TABLE或ALTER TABLE一起使用。所有分区必须属于相同的存储引擎。不支持将表分区分配给共享的 InnoDB 表空间。共享表空间包括 InnoDB 系统表空间和通用表空间。 -
通用表空间支持使用
CREATE TABLE ... TABLESPACE添加任何行格式的表。不需要启用innodb_file_per_table。 -
innodb_strict_mode不适用于通用表空间。表空间管理规则严格执行,与innodb_strict_mode无关。如果CREATE TABLESPACE参数不正确或不兼容,则无论innodb_strict_mode设置如何,操作都会失败。当使用CREATE TABLE ... TABLESPACE或ALTER TABLE ... TABLESPACE将表添加到通用表空间时,将忽略innodb_strict_mode,但该语句将被评估为启用了innodb_strict_mode。 -
使用
DROP TABLESPACE来删除表空间。在删除表空间之前,必须使用DROP TABLE删除表空间中的所有表。在删除 NDB Cluster 表空间之前,还必须使用一个或多个ALTER TABLESPACE ... DROP DATATFILE语句删除所有数据文件。参见 Section 25.6.11.1, “NDB Cluster Disk Data Objects”。 -
添加到 InnoDB 通用表空间的 InnoDB 表的所有部分都驻留在通用表空间中,包括索引和
BLOB页面。对于分配给表空间的 NDB 表,只有未建立索引的列存储在磁盘上,并实际使用表空间数据文件。所有 NDB 表的索引和已建立索引的列始终保留在内存中。
-
与系统表空间类似,截断或删除存储在通用表空间中的表会在通用表空间的.ibd 数据文件中创建内部的可用空间,该空间只能用于新的 InnoDB 数据。与文件-每表表空间不同,空间不会像对操作系统释放那样被释放。
-
通用表空间不与任何数据库或模式相关联。
-
ALTER TABLE ... DISCARD TABLESPACE和ALTER TABLE ...IMPORT TABLESPACE不支持属于通用表空间的表。 -
服务器对引用通用表空间的 DDL 使用表空间级元数据锁定。相比之下,服务器对引用每个表的文件表空间的 DDL 使用表级元数据锁定。
-
生成的或现有的表空间不能更改为通用表空间。
-
通用表空间名称与每个表的文件表空间名称之间没有冲突。通用表空间名称中不允许出现文件表空间名称中的“/”字符。
-
mysqldump 和 mysqlpump 不会转储
InnoDBCREATE TABLESPACE语句。
InnoDB 示例
此示例演示了创建一个通用表空间,并添加三个不同行格式的未压缩表。
mysql> CREATE TABLESPACE `ts1` ADD DATAFILE 'ts1.ibd' ENGINE=INNODB;
mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1 ROW_FORMAT=REDUNDANT;
mysql> CREATE TABLE t2 (c1 INT PRIMARY KEY) TABLESPACE ts1 ROW_FORMAT=COMPACT;
mysql> CREATE TABLE t3 (c1 INT PRIMARY KEY) TABLESPACE ts1 ROW_FORMAT=DYNAMIC;
此示例演示了创建一个通用表空间并添加一个压缩表。该示例假定默认的innodb_page_size值为 16K。8192 的FILE_BLOCK_SIZE要求压缩表具有 8 的KEY_BLOCK_SIZE。
mysql> CREATE TABLESPACE `ts2` ADD DATAFILE 'ts2.ibd' FILE_BLOCK_SIZE = 8192 Engine=InnoDB;
mysql> CREATE TABLE t4 (c1 INT PRIMARY KEY) TABLESPACE ts2 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;
此示例演示了创建一个通用表空间而不指定ADD DATAFILE子句,这在 MySQL 8.0.14 中是可选的。
mysql> CREATE TABLESPACE `ts3` ENGINE=INNODB;
此示例演示了创建撤销表空间。
mysql> CREATE UNDO TABLESPACE *undo_003* ADD DATAFILE '*undo_003*.ibu';
NDB 示例
假设您希望使用名为mydata-1.dat的数据文件创建一个名为myts的 NDB Cluster 磁盘数据表空间。一个NDB表空间总是需要使用一个或多个撤销日志文件组的日志文件。对于此示例,我们首先创建一个名为mylg的日志文件组,其中包含一个名为myundo-1.dat的撤销长文件,使用此处显示的CREATE LOGFILE GROUP语句:
mysql> CREATE LOGFILE GROUP myg1
-> ADD UNDOFILE 'myundo-1.dat'
-> ENGINE=NDB;
Query OK, 0 rows affected (3.29 sec)
现在,您可以使用以下语句创建先前描述的表空间:
mysql> CREATE TABLESPACE myts
-> ADD DATAFILE 'mydata-1.dat'
-> USE LOGFILE GROUP mylg
-> ENGINE=NDB;
Query OK, 0 rows affected (2.98 sec)
您现在可以使用带有TABLESPACE和STORAGE DISK选项的CREATE TABLE语句来创建一个磁盘数据表,类似于下面显示的内容:
mysql> CREATE TABLE mytable (
-> id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> lname VARCHAR(50) NOT NULL,
-> fname VARCHAR(50) NOT NULL,
-> dob DATE NOT NULL,
-> joined DATE NOT NULL,
-> INDEX(last_name, first_name)
-> )
-> TABLESPACE myts STORAGE DISK
-> ENGINE=NDB;
Query OK, 0 rows affected (1.41 sec)
需要注意的是,由于id、lname和fname列都被索引,因此mytable中只有dob和joined列实际上存储在磁盘上。
如前所述,当CREATE TABLESPACE与ENGINE [=] NDB一起使用时,将在每个 NDB Cluster 数据节点上创建一个表空间和相关的数据文件。您可以通过查询信息模式FILES表来验证数据文件是否已创建并获取有关它们的信息,如下所示:
mysql> SELECT FILE_NAME, FILE_TYPE, LOGFILE_GROUP_NAME, STATUS, EXTRA
-> FROM INFORMATION_SCHEMA.FILES
-> WHERE TABLESPACE_NAME = 'myts';
+--------------+------------+--------------------+--------+----------------+
| file_name | file_type | logfile_group_name | status | extra |
+--------------+------------+--------------------+--------+----------------+
| mydata-1.dat | DATAFILE | mylg | NORMAL | CLUSTER_NODE=5 |
| mydata-1.dat | DATAFILE | mylg | NORMAL | CLUSTER_NODE=6 |
| NULL | TABLESPACE | mylg | NORMAL | NULL |
+--------------+------------+--------------------+--------+----------------+
3 rows in set (0.01 sec)
有关更多信息和示例,请参见第 25.6.11.1 节,“NDB 集群磁盘数据对象”。
15.1.22 CREATE TRIGGER Statement
CREATE
[DEFINER = *user*]
TRIGGER [IF NOT EXISTS] *trigger_name*
*trigger_time* *trigger_event*
ON *tbl_name* FOR EACH ROW
[*trigger_order*]
*trigger_body*
*trigger_time*: { BEFORE | AFTER }
*trigger_event*: { INSERT | UPDATE | DELETE }
*trigger_order*: { FOLLOWS | PRECEDES } *other_trigger_name*
此语句创建一个新触发器。触发器是与表关联的命名数据库对象,当表发生特定事件时激活。触发器与名为 tbl_name 的表关联,该表必须引用永久表。您不能将触发器与 TEMPORARY 表或视图关联。
触发器名称存在于模式命名空间中,这意味着所有触发器在模式内必须具有唯一名称。不同模式中的触发器可以具有相同的名称。
IF NOT EXISTS 可以防止在同一模式中存在具有相同名称、在同一表上的触发器时发生错误。此选项从 MySQL 8.0.29 开始支持。
本节描述了 CREATE TRIGGER 语法。有关更多讨论,请参见 第 27.3.1 节“触发器语法和示例”。
CREATE TRIGGER 需要与触发器关联的表的 TRIGGER 权限。如果存在 DEFINER 子句,则所需的权限取决于 user 值,如 第 27.6 节“存储对象访问控制” 中所讨论的。如果启用了二进制日志记录,则 CREATE TRIGGER 可能需要 SUPER 权限,如 第 27.7 节“存储程序二进制日志记录” 中所述。
DEFINER 子句确定在触发器激活时用于检查访问权限的安全上下文,如本节后面所述。
trigger_time 是触发器动作时间。它可以是 BEFORE 或 AFTER,表示触发器在修改每行之前或之后激活。
在触发器激活之前会进行基本列值检查,因此您不能使用 BEFORE 触发器将不适合列类型的值转换为有效值。
trigger_event 表示激活触发器的操作类型。这些 trigger_event 值是允许的:
-
INSERT: 当向表中插入新行时触发器会激活(例如,通过INSERT、LOAD DATA和REPLACE语句)。 -
UPDATE: 当行被修改时触发器会激活(例如,通过UPDATE语句)。 -
DELETE:该触发器在从表中删除行时激活(例如,通过DELETE和REPLACE语句)。对表进行DROP TABLE和TRUNCATE TABLE操作不会激活该触发器,因为它们不使用DELETE。删除分区也不会激活DELETE触发器。
trigger_event 不代表激活触发器的 SQL 语句类型,而更多地代表一种表操作类型。例如,一个INSERT触发器不仅激活INSERT语句,还激活LOAD DATA语句,因为这两个语句都向表中插入行。
这种情况可能会令人困惑的一个例子是INSERT INTO ... ON DUPLICATE KEY UPDATE ...语法:一个BEFORE INSERT触发器为每一行激活,接着是一个AFTER INSERT触发器或者BEFORE UPDATE和AFTER UPDATE触发器,具体取决于该行是否存在重复键。
注意
级联外键操作不会触发触发器。
可以为给定表定义多个具有相同触发事件和动作时间的触发器。例如,可以为表定义两个BEFORE UPDATE触发器。默认情况下,具有相同触发事件和动作时间的触发器按照它们创建的顺序激活。要影响触发器顺序,请指定一个*trigger_order*子句,指示FOLLOWS或PRECEDES以及一个同样具有相同触发事件和动作时间的现有触发器的名称。使用FOLLOWS,新触发器在现有触发器之后激活。使用PRECEDES,新触发器在现有触发器之前激活。
trigger_body 是触发器激活时要执行的语句。要执行多个语句,请使用BEGIN ... END复合语句结构。这还使您能够使用存储过程中允许的相同语句。请参阅 Section 15.6.1, “BEGIN ... END Compound Statement”。某些语句在触发器中不允许使用;请参阅 Section 27.8, “Restrictions on Stored Programs”。
在触发器主体中,您可以通过使用别名OLD和NEW引用主题表(与触发器关联的表)中的列。OLD.*col_name*指的是更新或删除之前现有行的列。NEW.*col_name*指的是要插入的新行或更新后的现有行的列。
触发器不能使用NEW.*col_name*或使用OLD.*col_name*来引用生成列。有关生成列的信息,请参见第 15.1.20.8 节,“CREATE TABLE and Generated Columns”。
MySQL 在创建触发器时存储sql_mode系统变量设置,并始终以此设置执行触发器主体,无论触发器开始执行时当前服务器 SQL 模式如何。
DEFINER子句指定在触发器激活时检查访问权限时要使用的 MySQL 帐户。如果存在DEFINER子句,则*user值应为指定为'*user_name*'@'*host_name*'、CURRENT_USER或CURRENT_USER()的 MySQL 帐户。允许的user*值取决于您拥有的权限,如第 27.6 节“存储对象访问控制”中所讨论的。还请参阅该部分以获取有关触发器安全性的其他信息。
如果省略DEFINER子句,则默认定义者是执行CREATE TRIGGER语句的用户。这与明确指定DEFINER = CURRENT_USER相同。
MySQL 在检查触发器权限时考虑DEFINER用户如下:
-
在
CREATE TRIGGER时,发出该语句的用户必须具有TRIGGER权限。 -
在触发器激活时,权限将针对
DEFINER用户进行检查。此用户必须具有以下权限:-
主题表的
TRIGGER权限。 -
如果触发器主体中使用
OLD.*col_name*或NEW.*col_name*引用表列,则需要对主题表具有SELECT权限。 -
如果表列是触发器主体中
SET NEW.*col_name* = *value*的目标,则需要对主题表具有UPDATE权限。 -
触发器执行的语句通常需要的其他权限。
-
在触发器体内,CURRENT_USER函数返回在触发器激活时用于检查权限的账户。这是DEFINER用户,而不是触发器激活的用户。有关触发器内用户审计的信息,请参阅 Section 8.2.23, “SQL-Based Account Activity Auditing”。
如果你使用LOCK TABLES来锁定一个带有触发器的表,那么触发器中使用的表也会被锁定,就像在 LOCK TABLES and Triggers 中描述的那样。
欲了解更多关于触发器使用的讨论,请参阅 Section 27.3.1, “Trigger Syntax and Examples”。
15.1.23 创建视图语句
CREATE
[OR REPLACE]
[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语句创建一个新视图,如果给出OR REPLACE子句,则替换现有视图。如果视图不存在,CREATE OR REPLACE VIEW与CREATE VIEW相同。如果视图存在,CREATE OR REPLACE VIEW将其替换。
有关视图使用限制的信息,请参阅第 27.9 节,“视图限制”。
*select_statement是一个SELECT语句,提供了视图的定义。(从视图中选择实际上是使用SELECT语句进行选择。)select_statement*可以从基本表或其他视图中进行选择。从 MySQL 8.0.19 开始,SELECT语句可以使用VALUES语句作为其来源,或者可以被替换为TABLE语句,就像CREATE TABLE ... SELECT一样。
视图定义在创建时“冻结”,不受后续对基础表定义的更改的影响。例如,如果一个视图被定义为在表上SELECT *,那么稍后添加到表中的新列不会成为视图的一部分,而从表中删除的列在从视图中选择时会导致错误。
ALGORITHM子句影响 MySQL 处理视图的方式。DEFINER和SQL SECURITY子句指定在视图调用时检查访问权限时要使用的安全上下文。WITH CHECK OPTION子句可以用于限制对视图引用的表中的行的插入或更新。这些子句稍后在本节中描述。
CREATE VIEW语句需要视图的CREATE VIEW权限,并且对SELECT语句中选择的每个列都需要一些权限。对于在SELECT语句中的其他地方使用的列,您必须具有SELECT权限。如果存在OR REPLACE子句,则还必须具有视图的DROP权限。如果存在DEFINER子句,则所需的权限取决于*user*值,如第 27.6 节,“存储对象访问控制”中所讨论的那样。
当引用视图时,权限检查将按照本节后面描述的方式进行。
视图属于数据库。默认情况下,新视图将在默认数据库中创建。要在特定数据库中显式创建视图,请使用*db_name.view_name*语法,以数据库名称限定视图名称:
CREATE VIEW test.v AS SELECT * FROM t;
SELECT语句中的未限定表或视图名称也会根据默认数据库进行解释。视图可以通过使用适当的数据库名称限定表或视图名称来引用其他数据库中的表或视图。
在数据库中,基本表和视图共享相同的命名空间,因此基本表和视图不能具有相同的名称。
由SELECT语句检索的列可以是对表列的简单引用,也可以是使用函数、常量值、运算符等的表达式。
视图必须具有唯一的列名,不能有重复,就像基本表一样。默认情况下,由SELECT语句检索的列的名称用于视图列名。要为视图列定义显式名称,请指定可选的*column_list子句作为逗号分隔的标识符列表。column_list*中的名称数量必须与由SELECT语句检索的列的数量相同。
视图可以从许多种类的SELECT语句创建。它可以引用基本表或其他视图。它可以使用连接、UNION和子查询。SELECT甚至不需要引用任何表:
CREATE VIEW v_today (today) AS SELECT CURRENT_DATE;
以下示例定义了一个视图,从另一个表中选择了两列,以及从这些列计算出的表达式:
mysql> CREATE TABLE t (qty INT, price INT);
mysql> INSERT INTO t VALUES(3, 50);
mysql> CREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t;
mysql> SELECT * FROM v;
+------+-------+-------+
| qty | price | value |
+------+-------+-------+
| 3 | 50 | 150 |
+------+-------+-------+
视图定义受以下限制:
-
SELECT语句不能引用系统变量或用户定义的变量。 -
在存储程序中,
SELECT语句不能引用程序参数或局部变量。 -
SELECT语句不能引用准备好的语句参数。 -
定义中引用的任何表或视图必须存在。如果在创建视图之后,定义引用的表或视图被删除,则使用该视图会导致错误。要检查此类问题的视图定义,请使用
CHECK TABLE语句。 -
定义不能引用
TEMPORARY表,也不能创建TEMPORARY视图。 -
不能将触发器与视图关联。
-
在
SELECT语句中,列名的别名会被检查,其最大长度为 64 个字符(而不是最大别名长度为 256 个字符)。
视图定义中允许使用ORDER BY,但如果使用具有自己ORDER BY的语句从视图中进行选择,则会被忽略。
对于定义中的其他选项或子句,它们会被添加到引用视图的语句的选项或子句中,但效果是未定义的。例如,如果视图定义包括LIMIT子句,并且您使用具有自己LIMIT子句的语句从视图中进行选择,则未定义哪个限制适用。这个原则也适用于跟随SELECT关键字的ALL、DISTINCT或SQL_SMALL_RESULT等选项,以及诸如INTO、FOR UPDATE、FOR SHARE、LOCK IN SHARE MODE和PROCEDURE等子句。
如果更改查询处理环境,可能会影响从视图中获取的结果:
mysql> CREATE VIEW v (mycol) AS SELECT 'abc';
Query OK, 0 rows affected (0.01 sec)
mysql> SET sql_mode = '';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT "mycol" FROM v;
+-------+
| mycol |
+-------+
| mycol |
+-------+
1 row in set (0.01 sec)
mysql> SET sql_mode = 'ANSI_QUOTES';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT "mycol" FROM v;
+-------+
| mycol |
+-------+
| abc |
+-------+
1 row in set (0.00 sec)
DEFINER和SQL SECURITY子句确定在执行引用视图的语句时使用哪个 MySQL 账户来检查访问权限。有效的SQL SECURITY特性值为DEFINER(默认)和INVOKER。这表示所需的权限必须由定义或调用视图的用户持有。
如果存在DEFINER子句,则*user值应为 MySQL 账户,指定为'*user_name*'@'*host_name*、CURRENT_USER或CURRENT_USER()。允许的user*值取决于您拥有的权限,如第 27.6 节“存储对象访问控制”中所讨论的。还请参阅该部分以获取有关视图安全性的其他信息。
如果省略了DEFINER子句,则默认的定义者是执行CREATE VIEW语句的用户。这与明确指定DEFINER = CURRENT_USER相同。
在视图定义中,CURRENT_USER函数默认返回视图的DEFINER值。对于使用SQL SECURITY INVOKER特性定义的视图,CURRENT_USER返回视图调用者的账户。有关视图内用户审计的信息,请参阅第 8.2.23 节“基于 SQL 的账户活动审计”。
在使用SQL SECURITY DEFINER特性定义的存储过程中,CURRENT_USER返回该存储过程的DEFINER值。如果视图定义中包含CURRENT_USER的DEFINER值,这也会影响到在此类存储过程中定义的视图。
MySQL 检查视图权限的方式如下:
-
在视图定义时,视图创建者必须具有使用视图访问的顶层对象所需的权限。例如,如果视图定义引用表列,则创建者必须对定义中的每个列具有某些权限,并且对定义中其他地方使用的每个列都需要
SELECT权限。如果定义引用了一个存储函数,则只能检查调用函数所需的权限。在函数调用时需要的权限只能在执行时检查:对于不同的调用,函数内的不同执行路径可能被采取。 -
引用视图的用户必须具有适当的权限来访问它(
SELECT用于从中选择,INSERT用于插入等)。 -
当引用了一个视图时,会根据视图
DEFINER账户或调用者持有的权限进行对象访问权限检查,具体取决于SQL SECURITY特性是DEFINER还是INVOKER。 -
如果引用视图导致执行存储函数,则在函数内执行的语句的权限检查取决于函数的
SQL SECURITY特性是DEFINER还是INVOKER。如果安全特性是DEFINER,则函数以DEFINER账户的权限运行。如果特性是INVOKER,则函数以视图的SQL SECURITY特性确定的权限运行。
例如:一个视图可能依赖于一个存储函数,而该函数可能调用其他存储过程。例如,以下视图调用了一个存储函数f():
CREATE VIEW v AS SELECT * FROM t WHERE t.id = f(t.name);
假设f()包含如下语句:
IF name IS NULL then
CALL p1();
ELSE
CALL p2();
END IF;
在执行f()时,需要检查执行语句所需的权限。这可能意味着在f()内部执行时需要p1()或p2()的权限,具体取决于f()内的执行路径。这些权限必须在运行时检查,而需要拥有这些权限的用户由视图v和函数f()的SQL SECURITY值确定。
视图的DEFINER和SQL SECURITY子句是标准 SQL 的扩展。在标准 SQL 中,视图使用SQL SECURITY DEFINER规则处理。标准规定视图的定义者,即视图模式的所有者,获得视图的适用权限(例如,SELECT)并可以授予它们。MySQL 没有“模式所有者”的概念,因此 MySQL 添加了一个子句来标识定义者。DEFINER子句是一个扩展,其目的是拥有标准的内容;也就是说,永久记录谁定义了视图。这就是为什么默认的DEFINER值是视图创建者的帐户。
可选的ALGORITHM子句是 MySQL 对标准 SQL 的扩展。它影响 MySQL 处理视图的方式。ALGORITHM有三个值:MERGE、TEMPTABLE或UNDEFINED。有关更多信息,请参见 Section 27.5.2, “View Processing Algorithms”,以及 Section 10.2.2.4, “Optimizing Derived Tables, View References, and Common Table Expressions with Merging or Materialization”。
一些视图是可更新的。也就是说,你可以在UPDATE、DELETE或INSERT等语句中使用它们来更新底层表的内容。要使视图可更新,视图中的行与底层表中的行之间必须是一对一的关系。还有一些其他构造使视图不可更新。
视图中的生成列被认为是可更新的,因为可以对其进行赋值。但是,如果显式更新这样的列,唯一允许的值是DEFAULT。有关生成列的信息,请参见 Section 15.1.20.8, “CREATE TABLE and Generated Columns”。
可以为可更新视图提供WITH CHECK OPTION子句,以防止插入或更新行,除非select_statement中的WHERE子句为真。
在可更新视图的WITH CHECK OPTION子句中,LOCAL和CASCADED关键字确定了在视图以另一个视图的形式定义时进行检查测试的范围。LOCAL关键字将CHECK OPTION限制在正在定义的视图中。CASCADED会导致对底层视图的检查也被评估。当没有给出关键字时,默认值为CASCADED。
关于可更新视图和WITH CHECK OPTION子句的更多信息,请参见第 27.5.3 节,“可更新和可插入视图”,以及第 27.5.4 节,“带有 CHECK OPTION 子句的视图”。
15.1.24 DROP DATABASE 语句
DROP {DATABASE | SCHEMA} [IF EXISTS] *db_name*
DROP DATABASE会删除数据库中的所有表并删除数据库。对于这个语句要非常小心!要使用DROP DATABASE,您需要在数据库上拥有DROP权限。DROP SCHEMA是DROP DATABASE的同义词。
重要提示
当一个数据库被删除时,为该数据库专门授予的权限不会自动删除。必须手动删除它们。参见 Section 15.7.1.6, “GRANT Statement”。
IF EXISTS用于防止数据库不存在��发生错误。
如果默认数据库被删除,那么默认数据库将被取消设置(DATABASE()函数返回NULL)。
如果你在一个符号链接的数据库上使用DROP DATABASE,那么链接和原始数据库都会被删除。
DROP DATABASE会返回被删除的表的数量。
DROP DATABASE语句会从给定的数据库目录中删除 MySQL 在正常操作期间可能创建的文件和目录。这包括以下列表中显示的所有带有扩展名的文件:
-
.BAK -
.DAT -
.HSH -
.MRG -
.MYD -
.MYI -
.cfg -
.db -
.ibd -
.ndb
如果在 MySQL 删除了刚列出的文件后,数据库目录中仍然存在其他文件或目录,则无法删除数据库目录。在这种情况下,您必须手动删除任何剩余的文件或目录,并再次发出DROP DATABASE语句。
删除数据库不会移除在该数据库中创建的任何TEMPORARY表。TEMPORARY表在创建它们的会话结束时会自动删除。参见 Section 15.1.20.2, “CREATE TEMPORARY TABLE Statement”。
你也可以使用mysqladmin来删除数据库。参见 Section 6.5.2, “mysqladmin — A MySQL Server Administration Program”。
15.1.25 删除事件语句
DROP EVENT [IF EXISTS] *event_name*
这个语句会删除名为*event_name*的事件。该事件立即停止活动,并从服务器完全删除。
如果事件不存在,则会出现错误 ERROR 1517 (HY000): 未知事件 'event_name'。您可以使用 IF EXISTS 来覆盖此错误,并使语句对不存在的事件生成警告。
这个语句需要对要删除事件所属模式的EVENT权限。
15.1.26 DROP FUNCTION 语句
DROP FUNCTION 语句用于删除存储函数和可加载函数:
-
有关删除存储函数的信息,请参阅第 15.1.29 节,“DROP PROCEDURE 和 DROP FUNCTION 语句”。
-
有关删除可加载函数的信息,请参阅第 15.7.4.2 节,“可加载函数的 DROP FUNCTION 语句”。
15.1.27 DROP INDEX 语句
DROP INDEX *index_name* ON *tbl_name*
[*algorithm_option* | *lock_option*] ...
*algorithm_option*:
ALGORITHM [=] {DEFAULT | INPLACE | COPY}
*lock_option*:
LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}
DROP INDEX 从表 tbl_name 中删除名为 index_name 的索引。此语句被映射为一个 ALTER TABLE 语句来删除索引。请参见 Section 15.1.9, “ALTER TABLE Statement”。
要删除主键,索引名称始终为 PRIMARY,必须将其指定为带引号的标识符,因为 PRIMARY 是一个保留字:
DROP INDEX `PRIMARY` ON t;
对于NDB表中的可变宽度列的索引是在线删除的;也就是说,不需要进行任何表复制。尽管表在操作期间针对相同的 API 节点被锁定,但不会阻止其他 NDB Cluster API 节点访问该表。服务器会在确定可能进行此操作时自动执行;您不需要使用任何特殊的 SQL 语法或服务器选项来触发此操作。
ALGORITHM 和 LOCK 子句可以用来影响表复制方法和读写表时的并发级别,当其索引正在被修改时。它们与ALTER TABLE语句具有相同的含义。更多信息,请参见 Section 15.1.9, “ALTER TABLE Statement”
MySQL NDB Cluster 支持使用标准 MySQL Server 中支持的相同 ALGORITHM=INPLACE 语法进行在线操作。更多信息,请参见 Section 25.6.12, “Online Operations with ALTER TABLE in NDB Cluster”。
第 15.1.28 节 DROP LOGFILE GROUP 语句
DROP LOGFILE GROUP *logfile_group*
ENGINE [=] *engine_name*
此语句删除名为*logfile_group*的日志文件组。日志文件组必须已经存在,否则将出现错误。(有关创建日志文件组的信息,请参见第 15.1.16 节,“CREATE LOGFILE GROUP 语句”。)
重要提示
在删除日志文件组之前,您必须删除所有使用该日志文件组进行UNDO日志记录的表空间。
必需的ENGINE子句提供了要删除的日志文件组所使用的存储引擎的名称。目前,*engine_name*的唯一允许值为NDB和NDBCLUSTER。
DROP LOGFILE GROUP仅适用于 NDB Cluster 的 Disk Data 存储。请参阅第 25.6.11 节,“NDB Cluster Disk Data Tables”。
15.1.29 DROP PROCEDURE and DROP FUNCTION Statements
DROP {PROCEDURE | FUNCTION} [IF EXISTS] *sp_name*
这些语句用于删除存储例程(存储过程或函数)。也就是说,指定的例程将从服务器中移除。(DROP FUNCTION 也用于删除可加载函数;参见 Section 15.7.4.2, “DROP FUNCTION Statement for Loadable Functions”.)
要删除存储例程,您必须具有 ALTER ROUTINE 权限。(如果启用了 automatic_sp_privileges 系统变量,则在创建例程时自动授予该权限和 EXECUTE 给例程创建者,并在删除例程时从创建者那里撤销。请参见 Section 27.2.2, “Stored Routines and MySQL Privileges”.)
另外,如果例程的定义者具有 SYSTEM_USER 权限,则删除它的用户也必须具有此权限。这在 MySQL 8.0.16 及更高版本中执行。
IF EXISTS 子句是 MySQL 的扩展。如果存储过程或函数不存在,它可以防止错误发生。会产生一个警告,可以通过 SHOW WARNINGS 查看。
DROP FUNCTION 也用于删除可加载函数(参见 Section 15.7.4.2, “DROP FUNCTION Statement for Loadable Functions”).
15.1.30 DROP SERVER 语句
DROP SERVER [ IF EXISTS ] *server_name*
删除名为*server_name*的服务器定义。mysql.servers表中的相应行将被删除。此语句需要SUPER权限。
删除表的服务器不会影响任何在创建时使用此连接信息的FEDERATED表。请参阅第 15.1.18 节,“CREATE SERVER Statement”。
DROP SERVER 会导致隐式提交。请参阅第 15.3.3 节,“导致隐式提交的语句”。
DROP SERVER 不会被写入二进制日志,无论使用的日志格式是什么。
15.1.31 删除空间参考系统语句
原文:
dev.mysql.com/doc/refman/8.0/en/drop-spatial-reference-system.html
DROP SPATIAL REFERENCE SYSTEM
[IF EXISTS]
*srid*
*srid*: *32-bit unsigned integer*
此语句从数据字典中删除一个空间参考系统(SRS)定义。它需要SUPER权限。
示例:
DROP SPATIAL REFERENCE SYSTEM 4120;
如果不存在具有 SRID 值的 SRS 定义,则会发生错误,除非指定了 IF EXISTS。在这种情况下,会发出警告而不是错误。
如果某个现有表中的某列使用了 SRID 值,则会发生错误。例如:
mysql> DROP SPATIAL REFERENCE SYSTEM 4326;
ERROR 3716 (SR005): Can't modify SRID 4326\. There is at
least one column depending on it.
要确定哪些列使用了 SRID,请使用以下查询:
SELECT * FROM INFORMATION_SCHEMA.ST_GEOMETRY_COLUMNS WHERE SRS_ID=4326;
SRID 值必须在 32 位无符号整数范围内,有以下限制:
-
SRID 0 是一个有效的 SRID,但不能与
删除空间参考系统一起使用。 -
如果值在保留的 SRID 范围内,会发出警告。保留范围为 [0, 32767](由 EPSG 保留)、[60,000,000, 69,999,999](由 EPSG 保留)和 [2,000,000,000, 2,147,483,647](由 MySQL 保留)。EPSG 代表欧洲石油调查组。
-
用户不应删除具有保留范围内 SRID 的 SRS。如果删除了系统安装的 SRS,则可能会在 MySQL 升级时重新创建 SRS 定义。
15.1.32 DROP TABLE 语句
DROP [TEMPORARY] TABLE [IF EXISTS]
*tbl_name* [, *tbl_name*] ...
[RESTRICT | CASCADE]
DROP TABLE 可以移除一个或多个表。对于每个表,您必须拥有 DROP 权限。
请谨慎 使用这个语句!对于每个表格,它会删除表格定义和所有表格数据。如果表格被分区,该语句会删除表格定义、所有分区、存储在这些分区中的所有数据以及与被删除表格相关的所有分区定义。
删除表格也会删除表格的任何触发器。
DROP TABLE 会导致隐式提交,除非与 TEMPORARY 关键字一起使用。请参阅 Section 15.3.3, “Statements That Cause an Implicit Commit”。
重要
当删除表格时,专门为表格授予的权限 不会 自动删除。必须手动删除它们。请参阅 Section 15.7.1.6, “GRANT Statement”。
如果参数列表中命名的任何表格不存在,则 DROP TABLE 的行为取决于是否给出 IF EXISTS 子句:
-
没有
IF EXISTS,该语句将因无法删除不存在的表格而失败,并且不会进行任何更改。 -
使用
IF EXISTS,对于不存在的表格不会发生错误。该语句会删除所有存在的命名表格,并为每个不存在的表格生成一个NOTE诊断信息。这些注释可以通过SHOW WARNINGS显示。请参阅 Section 15.7.7.42, “SHOW WARNINGS Statement”。
在一些异常情况下,IF EXISTS 对于删除表格也是有用的,即在数据字典中存在条目但存储引擎中没有管理的表格的情况下。(例如,如果在从存储引擎中删除表格后但在删除数据字典条目之前发生异常服务器退出的情况。)
TEMPORARY 关键字具有以下效果:
-
该语句仅删除
TEMPORARY表。 -
该语句不会导致隐式提交。
-
不会检查访问权限。
TEMPORARY表只能在创建它的会话中可见,因此不需要检查。
包含 TEMPORARY 关键字是防止意外删除非TEMPORARY表的好方法。
RESTRICT 和 CASCADE 关键字无效。它们被允许是为了更容易从其他数据库系统进行移植。
DROP TABLE 不支持所有 innodb_force_recovery 设置。请参阅 Section 17.21.3, “Forcing InnoDB Recovery”。
15.1.33 DROP TABLESPACE Statement
DROP [UNDO] TABLESPACE *tablespace_name*
[ENGINE [=] *engine_name*]
此语句删除先前使用CREATE TABLESPACE创建的表空间。它受NDB和InnoDB存储引擎支持。
在 MySQL 8.0.14 中引入的UNDO关键字必须在删除撤销表空间时指定。只能删除使用CREATE UNDO TABLESPACE语法创建的撤销表空间。撤销表空间必须处于空状态才能删除。有关更多信息,请参见 Section 17.6.3.4, “Undo Tablespaces”。
ENGINE设置使用表空间的存储引擎,其中*engine_name*是存储引擎的名称。目前,支持值为InnoDB和NDB。如果未设置,则使用default_storage_engine的值。如果与用于创建表空间的存储引擎不同,则DROP TABLESPACE语句将失败。
*tablespace_name*是 MySQL 中区分大小写的标识符。
对于InnoDB通用表空间,在执行DROP TABLESPACE操作之前,必须从表空间中删除所有表。如果表空间不为空,DROP TABLESPACE会返回错误。
要删除的NDB表空间不能包含任何数据文件;换句话说,在您可以删除NDB表空间之前,必须先使用ALTER TABLESPACE ... DROP DATAFILE命令删除每个数据文件。
注意
-
当表空间中的最后一个表被删除时,通用
InnoDB表空间不会自动删除。必须使用DROP TABLESPACE *tablespace_name*显式删除表空间。 -
DROP DATABASE操作可以删除属于通用表空间的表,但无法删除表空间,即使操作删除了属于表空间的所有表。必须使用DROP TABLESPACE *tablespace_name*显式删除表空间。 -
与系统表空间类似,截断或删除存储在通用表空间中的表会在通用表空间的.ibd 数据文件中创建内部的可用空间,该空间只能用于新的
InnoDB数据。与针对每个表的文件表空间不同,空间不会像对操作系统一样释放回去。
InnoDB 示例
这个示例演示了如何删除一个InnoDB通用表空间。通用表空间ts1是用一个表创建的。在删除表空间之前,必须先删除表。
mysql> CREATE TABLESPACE `ts1` ADD DATAFILE 'ts1.ibd' Engine=InnoDB;
mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1 Engine=InnoDB;
mysql> DROP TABLE t1;
mysql> DROP TABLESPACE ts1;
本示例演示了如何删除一个撤销表空间。撤销表空间必须处于空状态才能被删除。有关更多信息,请参见第 17.6.3.4 节,“Undo Tablespaces”。
mysql> DROP UNDO TABLESPACE *undo_003*;
NDB 示例
本示例演示了如何在首先创建表空间后删除一个NDB表空间myts,假设存在一个名为mylg的日志文件组(参见第 15.1.16 节,“CREATE LOGFILE GROUP Statement”)。
mysql> CREATE TABLESPACE myts
-> ADD DATAFILE 'mydata-1.dat'
-> USE LOGFILE GROUP mylg
-> ENGINE=NDB;
必须使用ALTER TABLESPACE语句,如下所示,从表空间中删除所有数据文件,然后才能删除表空间:
mysql> ALTER TABLESPACE myts
-> DROP DATAFILE 'mydata-1.dat'
-> ENGINE=NDB;
mysql> DROP TABLESPACE myts;
15.1.34 DROP TRIGGER Statement
DROP TRIGGER [IF EXISTS] [*schema_name*.]*trigger_name*
这个语句会触发一个触发器。模式(数据库)名称是可选的。如果省略了模式,触发器将从默认模式中删除。DROP TRIGGER需要与触发器关联的表的TRIGGER权限。
使用IF EXISTS可以防止出现触发器不存在的错误。在使用IF EXISTS时,会为不存在的触发器生成一个NOTE。参见 Section 15.7.7.42, “SHOW WARNINGS Statement”。
如果删除表,则该表的触发器也会被删除。
15.1.35 DROP VIEW Statement
DROP VIEW [IF EXISTS]
*view_name* [, *view_name*] ...
[RESTRICT | CASCADE]
DROP VIEW用于移除一个或多个视图。对于每个视图,您必须具有DROP权限。
如果参数列表中命名的任何视图不存在,则该语句将失败,并显示一个错误,指示无法删除的不存在视图的名称,并且不会进行任何更改。
注意
在 MySQL 5.7 及更早版本中,如果参数列表中命名的任何视图不存在,DROP VIEW会返回错误,但也会删除列表中存在的所有视图。由于 MySQL 8.0 中行为的更改,当在 MySQL 5.7 复制源服务器上复制到 MySQL 8.0 副本时,对于部分完成的DROP VIEW操作会失败。为避免此失败场景,在DROP VIEW语句中使用IF EXISTS语法可防止出现视图不存在的错误。更多信息,请参见 Section 15.1.1, “Atomic Data Definition Statement Support”。
IF EXISTS子句可防止出现视图不存在的错误。当使用此子句时,对于每个不存在的视图会生成一个NOTE。参见 Section 15.7.7.42, “SHOW WARNINGS Statement”。
RESTRICT和CASCADE,如果给定,将被解析并忽略。