MySQL8-中文参考-一百零一-

90 阅读1小时+

MySQL8 中文参考(一百零一)

原文:docs.oracle.com/javase/tutorial/reallybigindex.html

26.3.4 分区的维护

译文:dev.mysql.com/doc/refman/8.0/en/partitioning-maintenance.html

可以使用针对此类目的而设计的 SQL 语句在分区表上执行许多表和分区维护任务。

可以使用支持分区表的 CHECK TABLEOPTIMIZE TABLEANALYZE TABLEREPAIR TABLE 语句来完成分区表的表维护。

您可以使用一些扩展功能来直接在一个或多个分区上执行此类操作,如下列表所述:

  • 重建分区。 重建分区;这与删除分区中存储的所有记录,然后重新插入它们具有相同效果。这对于碎片整理很有用。

    示例:

    ALTER TABLE t1 REBUILD PARTITION p0, p1;
    
  • 优化分区。 如果您从分区中删除了大量行,或者对具有可变长度行的分区表进行了许多更改(即具有 VARCHARBLOBTEXT 列),您可以使用 ALTER TABLE ... OPTIMIZE PARTITION 来回收未使用的空间并对分区数据文件进行碎片整理。

    示例:

    ALTER TABLE t1 OPTIMIZE PARTITION p0, p1;
    

    在给定分区上使用 OPTIMIZE PARTITION 相当于在该分区上运行 CHECK PARTITIONANALYZE PARTITIONREPAIR PARTITION

    一些 MySQL 存储引擎,包括 InnoDB,不支持每个分区的优化;在这些情况下,ALTER TABLE ... OPTIMIZE PARTITION 会分析并重建整个表,并发出适当的警告(Bug #11751825, Bug #42822)。为避免此问题,请改用 ALTER TABLE ... REBUILD PARTITIONALTER TABLE ... ANALYZE PARTITION

  • 分析分区。 这读取并存储分区的键分布。

    示例:

    ALTER TABLE t1 ANALYZE PARTITION p3;
    
  • 修复分区。 这修复损坏的分区。

    示例:

    ALTER TABLE t1 REPAIR PARTITION p0,p1;
    

    通常情况下,当分区包含重复键错误时,REPAIR PARTITION 操作会失败。您可以使用 ALTER IGNORE TABLE 选项,此时由于存在重复键而无法移动的所有行将从分区中删除(Bug #16900947)。

  • 检查分区。 您可以以与对非分区表使用CHECK TABLE相同的方式检查分区中的错误。

    示例:

    ALTER TABLE trb3 CHECK PARTITION p1;
    

    这个语句告诉您表t1中分区p1中的数据或索引是否损坏。如果是这种情况,请使用ALTER TABLE ... REPAIR PARTITION来修复该分区。

    通常,当分区包含重复键错误时,CHECK PARTITION会失败。您可以在此选项中使用ALTER IGNORE TABLE,在这种情况下,该语句将返回在发现重复键违规的分区中每一行的内容。仅报告表的分区表达式中的列的值。(Bug #16900947)

列出的每个语句还支持关键字ALL代替分区名称列表。使用ALL会导致该语句作用于表中的所有分区。

您还可以使用ALTER TABLE ... TRUNCATE PARTITION截断分区。该语句可用于删除一个或多个分区中的所有行,方式与TRUNCATE TABLE删除表中的所有行类似。

ALTER TABLE ... TRUNCATE PARTITION ALL截断表中的所有分区。

26.3.5 获取有关分区的信息

原文:dev.mysql.com/doc/refman/8.0/en/partitioning-info.html

本节讨论获取有关现有分区的信息,可以通过多种方式进行。获取此类信息的方法包括以下内容:

  • 使用SHOW CREATE TABLE语句查看创建分区表时使用的分区子句。

  • 使用SHOW TABLE STATUS语句来确定表是否被分区。

  • 查询信息模式PARTITIONS表。

  • 使用语句EXPLAIN SELECT来查看给定SELECT使用的分区。

从 MySQL 8.0.16 开始,当对分区表进行插入、删除或更新操作时,二进制日志记录有关发生行事件的分区和(如果有)子分区的信息。即使涉及的表相同,也为发生在不同分区或子分区中的修改创建新的行事件。因此,如果一个事务涉及三个分区或子分区,将生成三个行事件。对于更新事件,分区信息被记录在“之前”图像和“之后”图像中。如果在使用mysqlbinlog查看二进制日志时指定了-v--verbose选项,则会显示分区信息。仅当使用基于行的日志记录时(binlog_format=ROW)才记录分区信息。

如本章其他地方所讨论的,SHOW CREATE TABLE在其输出中包含用于创建分区表的PARTITION BY子句。例如:

mysql> SHOW CREATE TABLE trb3\G
*************************** 1\. row ***************************
       Table: trb3
Create Table: CREATE TABLE `trb3` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(50) DEFAULT NULL,
  `purchased` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
/*!50100 PARTITION BY RANGE (YEAR(purchased))
(PARTITION p0 VALUES LESS THAN (1990) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (1995) ENGINE = InnoDB,
 PARTITION p2 VALUES LESS THAN (2000) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (2005) ENGINE = InnoDB) */ 0 row in set (0.00 sec)

对于分区表,SHOW TABLE STATUS的输出与非分区表相同,只是Create_options列包含字符串partitionedEngine列包含表的所有分区使用的存储引擎的名称。(有关此语句的更多信息,请参见第 15.7.7.38 节,“SHOW TABLE STATUS Statement”。)

您还可以从INFORMATION_SCHEMA中获取有关分区的信息,其中包含一个PARTITIONS表。请参阅第 28.3.21 节,“INFORMATION_SCHEMA PARTITIONS 表”。

可以通过使用解释来确定分区表中哪些分区涉及给定的SELECT查询。解释输出中的partitions列列出了查询将匹配的记录的分区。

假设创建并填充了一个名为trb1的表如下:

CREATE TABLE trb1 (id INT, name VARCHAR(50), purchased DATE)
    PARTITION BY RANGE(id)
    (
        PARTITION p0 VALUES LESS THAN (3),
        PARTITION p1 VALUES LESS THAN (7),
        PARTITION p2 VALUES LESS THAN (9),
        PARTITION p3 VALUES LESS THAN (11)
    );

INSERT INTO trb1 VALUES
    (1, 'desk organiser', '2003-10-15'),
    (2, 'CD player', '1993-11-05'),
    (3, 'TV set', '1996-03-10'),
    (4, 'bookcase', '1982-01-10'),
    (5, 'exercise bike', '2004-05-09'),
    (6, 'sofa', '1987-06-05'),
    (7, 'popcorn maker', '2001-11-22'),
    (8, 'aquarium', '1992-08-04'),
    (9, 'study desk', '1984-09-16'),
    (10, 'lava lamp', '1998-12-25');

你可以看到在查询中使用了哪些分区,比如SELECT * FROM trb1;,如下所示:

mysql> EXPLAIN SELECT * FROM trb1\G
*************************** 1\. row ***************************
           id: 1
  select_type: SIMPLE
        table: trb1
   partitions: p0,p1,p2,p3
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10
        Extra: Using filesort

在这种情况下,所有四个分区都被搜索。然而,当在查询中添加使用分区键的限制条件时,您可以看到只有包含匹配值的分区被扫描,如下所示:

mysql> EXPLAIN SELECT * FROM trb1 WHERE id < 5\G
*************************** 1\. row ***************************
           id: 1
  select_type: SIMPLE
        table: trb1
   partitions: p0,p1
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10
        Extra: Using where

解释还提供了有关使用的键和可能的键的信息:

mysql> ALTER TABLE trb1 ADD PRIMARY KEY (id);
Query OK, 10 rows affected (0.03 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> EXPLAIN SELECT * FROM trb1 WHERE id < 5\G
*************************** 1\. row ***************************
           id: 1
  select_type: SIMPLE
        table: trb1
   partitions: p0,p1
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 7
        Extra: Using where

如果使用解释来检查针对非分区表的查询,不会产生错误,但partitions列的值始终为NULL

解释输出的rows列显示表中的总行数。

另请参阅第 15.8.2 节,“解释语句”。

26.4 分区修剪

原文:dev.mysql.com/doc/refman/8.0/en/partitioning-pruning.html

分区修剪优化基于一个相对简单的概念,可以描述为“不要扫描那些不可能有匹配值的分区”。假设通过以下语句创建了一个分区表t1

CREATE TABLE t1 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY RANGE( region_code ) (
    PARTITION p0 VALUES LESS THAN (64),
    PARTITION p1 VALUES LESS THAN (128),
    PARTITION p2 VALUES LESS THAN (192),
    PARTITION p3 VALUES LESS THAN MAXVALUE
);

假设您希望从类似于以下语句的SELECT语句中获取结果:

SELECT fname, lname, region_code, dob
    FROM t1
    WHERE region_code > 125 AND region_code < 130;

很容易看出,应该返回的行中没有任何位于分区p0p3中的行;也就是说,我们只需要在分区p1p2中搜索匹配的行。通过限制搜索范围,可以比扫描表中的所有分区更少的时间和精力来找到匹配的行。这种“剪掉”不需要的分区的操作称为修剪。当优化器在执行此查询时可以利用分区修剪时,查询的执行速度可能比针对包含相同列定义和数据的非分区表的相同查询快一个数量级。

优化器可以在WHERE条件可以简化为以下两种情况之一时执行修剪:

  • *partition_column* = *constant*

  • *partition_column* IN (*constant1*, *constant2*, ..., *constantN*)

在第一种情况下,优化器简单地对给定值评估分区表达式,确定包含该值的分区,并仅扫描此分区。在许多情况下,等号可以替换为另一个算术比较,包括<><=>=<>。一些在WHERE子句中使用BETWEEN的查询也可以利用分区修剪。请参见本节后面的示例。

在第二种情况下,优化器对列表中的每个值评估分区表达式,创建一个匹配分区的列表,然后仅扫描此分区列表中的分区。

SELECTDELETEUPDATE语句支持分区修剪。INSERT语句也仅访问每个插入行的一个分区;即使对于使用HASHKEY进行分区的表,这也是正确的,尽管目前在EXPLAIN的输出中没有显示。

修剪也可以应用于短范围,优化器可以将其转换为等效值列表。例如,在前面的例子中,WHERE 子句可以转换为 WHERE region_code IN (126, 127, 128, 129)。然后优化器可以确定列表中的前两个值位于分区 p1 中,剩下的两个值位于分区 p2 中,其他分区不包含相关值,因此不需要搜索匹配行。

优化器还可以对使用 RANGE COLUMNSLIST COLUMNS 分区的表上涉及多列比较的 WHERE 条件执行修剪。

只要分区表达式由相等性或可减少为一组相等性的范围组成,或者分区表达式表示递增或递减关系,就可以应用这种优化。当分区表达式使用 YEAR()TO_DAYS() 函数时,也可以应用修剪到基于 DATEDATETIME 列分区的表。当分区表达式使用 TO_SECONDS() 函数时,也可以应用修剪到这样的表。

假设表 t2,根据一个 DATE 列进行分区,是使用以下语句创建的:

CREATE TABLE t2 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY RANGE( YEAR(dob) ) (
    PARTITION d0 VALUES LESS THAN (1970),
    PARTITION d1 VALUES LESS THAN (1975),
    PARTITION d2 VALUES LESS THAN (1980),
    PARTITION d3 VALUES LESS THAN (1985),
    PARTITION d4 VALUES LESS THAN (1990),
    PARTITION d5 VALUES LESS THAN (2000),
    PARTITION d6 VALUES LESS THAN (2005),
    PARTITION d7 VALUES LESS THAN MAXVALUE
);

以下使用 t2 的语句可以利用分区修剪:

SELECT * FROM t2 WHERE dob = '1982-06-23';

UPDATE t2 SET region_code = 8 WHERE dob BETWEEN '1991-02-15' AND '1997-04-25';

DELETE FROM t2 WHERE dob >= '1984-06-21' AND dob <= '1999-06-21'

对于最后一条语句,优化器还可以执行以下操作:

  1. 找到包含范围低端的分区

    YEAR('1984-06-21') 返回值为 1984,该值位于分区 d3 中。

  2. 找到包含范围高端的分区

    YEAR('1999-06-21') 计算结果为 1999,该值位于分区 d5 中。

  3. 仅扫描这两个分区以及可能位于它们之间的任何分区

    在这种情况下,这意味着只有分区 d3d4d5 被扫描。其余分区可以安全地被忽略(并且被忽略)。

重要提示

对于针对分区表的语句中 WHERE 条件引用的无效 DATEDATETIME 值,将被视为 NULL。这意味着像 SELECT * FROM *partitioned_table* WHERE *date_column* < '2008-12-00' 这样的查询不会返回任何值(参见 Bug #40972)。

到目前为止,我们只看过使用 RANGE 分区的示例,但修剪也可以应用于其他分区类型。

考虑一个由LIST分区的表,其中分区表达式是递增或递减的,例如这里显示的表t3。(在本例中,为简洁起见,我们假设region_code列的值限定在 1 到 10 之间。)

CREATE TABLE t3 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY LIST(region_code) (
    PARTITION r0 VALUES IN (1, 3),
    PARTITION r1 VALUES IN (2, 5, 8),
    PARTITION r2 VALUES IN (4, 9),
    PARTITION r3 VALUES IN (6, 7, 10)
);

对于像SELECT * FROM t3 WHERE region_code BETWEEN 1 AND 3这样的语句,优化器确定值 1、2 和 3 在哪些分区中找到(r0r1),并跳过其余的分区(r2r3)。

对于由HASH[LINEAR] KEY分区的表,如果WHERE子句使用简单的=关系针对分区表达式中使用的列,则也可以进行分区修剪。考虑创建如下表:

CREATE TABLE t4 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY KEY(region_code)
PARTITIONS 8;

将列值与常量进行比较的语句可以被修剪:

UPDATE t4 WHERE region_code = 7;

对于短范围,修剪也可以用于,因为优化器可以将这种条件转换为IN关系。例如,使用之前定义的相同表t4,可以修剪以下查询:

SELECT * FROM t4 WHERE region_code > 2 AND region_code < 6;

SELECT * FROM t4 WHERE region_code BETWEEN 3 AND 5;

在这两种情况下,优化器将WHERE子句转换为WHERE region_code IN (3, 4, 5)

重要

只有当范围大小小于分区数时才使用这种优化。考虑这个语句:

DELETE FROM t4 WHERE region_code BETWEEN 4 AND 12;

WHERE子句中的范围涵盖了 9 个值(4, 5, 6, 7, 8, 9, 10, 11, 12),但t4只有 8 个分区。这意味着DELETE无法被修剪。

当表由HASH[LINEAR] KEY分区时,修剪只能用于整数列。例如,这个语句不能使用修剪,因为dob是一个DATE列:

SELECT * FROM t4 WHERE dob >= '2001-04-14' AND dob <= '2005-10-15';

然而,如果表在INT列中存储年份值,则查询WHERE year_col >= 2001 AND year_col <= 2005可以被修剪。

使用提供自动分区的存储引擎的表,例如 MySQL Cluster 使用的NDB存储引擎,如果它们被显式分区,则可以被修剪。

26.5 分区选择

原文:dev.mysql.com/doc/refman/8.0/en/partitioning-selection.html

支持为匹配给定WHERE条件的行显式选择分区和子分区。分区选择类似于分区修剪,只检查特定分区是否匹配,但在两个关键方面有所不同:

  1. 要检查的分区由语句的发出者指定,与自动分区修剪不同。

  2. 分区修剪仅适用于查询,而显式选择分区支持查询和一些 DML 语句。

支持显式分区选择的 SQL 语句列在此处:

  • SELECT

  • DELETE

  • INSERT

  • REPLACE

  • UPDATE

  • LOAD DATA

  • LOAD XML

本节的其余部分讨论了显式分区选择,通常适用于刚刚列出的语句,并提供了一些示例。

显式分区选择是使用PARTITION选项实现的。对于所有支持的语句,此选项使用以下语法:

 PARTITION (*partition_names*)

      *partition_names*:
          *partition_name*, ...

此选项始终跟随分区或子分区所属的表的名称。partition_names是要使用的分区或子分区的逗号分隔列表。此列表中的每个名称必须是指定表的现有分区或子分区的名称;如果找不到任何分区或子分区,则该语句将失败并显示错误(分区'partition_name'不存在)。*partition_names*中列出的分区和子分区可以以任何顺序列出,并且可以重叠。

使用PARTITION选项时,仅检查列出的分区和子分区以匹配行。此选项可用于SELECT语句,以确定哪些行属于给定分区。考虑一个名为employees的分区表,使用以下语句创建和填充:

SET @@SQL_MODE = '';

CREATE TABLE employees  (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    fname VARCHAR(25) NOT NULL,
    lname VARCHAR(25) NOT NULL,
    store_id INT NOT NULL,
    department_id INT NOT NULL
)
    PARTITION BY RANGE(id)  (
        PARTITION p0 VALUES LESS THAN (5),
        PARTITION p1 VALUES LESS THAN (10),
        PARTITION p2 VALUES LESS THAN (15),
        PARTITION p3 VALUES LESS THAN MAXVALUE
);

INSERT INTO employees VALUES
    ('', 'Bob', 'Taylor', 3, 2), ('', 'Frank', 'Williams', 1, 2),
    ('', 'Ellen', 'Johnson', 3, 4), ('', 'Jim', 'Smith', 2, 4),
    ('', 'Mary', 'Jones', 1, 1), ('', 'Linda', 'Black', 2, 3),
    ('', 'Ed', 'Jones', 2, 1), ('', 'June', 'Wilson', 3, 1),
    ('', 'Andy', 'Smith', 1, 3), ('', 'Lou', 'Waters', 2, 4),
    ('', 'Jill', 'Stone', 1, 4), ('', 'Roger', 'White', 3, 2),
    ('', 'Howard', 'Andrews', 1, 2), ('', 'Fred', 'Goldberg', 3, 3),
    ('', 'Barbara', 'Brown', 2, 3), ('', 'Alice', 'Rogers', 2, 2),
    ('', 'Mark', 'Morgan', 3, 3), ('', 'Karen', 'Cole', 3, 2);

您可以这样查看存储在分区p1中的行:

mysql> SELECT * FROM employees PARTITION (p1);
+----+-------+--------+----------+---------------+
| id | fname | lname  | store_id | department_id |
+----+-------+--------+----------+---------------+
|  5 | Mary  | Jones  |        1 |             1 |
|  6 | Linda | Black  |        2 |             3 |
|  7 | Ed    | Jones  |        2 |             1 |
|  8 | June  | Wilson |        3 |             1 |
|  9 | Andy  | Smith  |        1 |             3 |
+----+-------+--------+----------+---------------+
5 rows in set (0.00 sec)

结果与通过查询SELECT * FROM employees WHERE id BETWEEN 5 AND 9获得的结果相同。

要从多个分区获取行,请将它们的名称作为逗号分隔的列表提供。例如,SELECT * FROM employees PARTITION (p1, p2)返回分区p1p2中的所有行,同时排除其余分区的行。

对分区表的任何有效查询都可以通过PARTITION选项重写,以限制结果为一个或多个所需的分区。您可以使用WHERE条件、ORDER BYLIMIT选项等。您还可以使用带有HAVINGGROUP BY选项的聚合函数。在先前定义的employees表上运行以下每个查询都会产生有效结果:

mysql> SELECT * FROM employees PARTITION (p0, p2)
 ->     WHERE lname LIKE 'S%';
+----+-------+-------+----------+---------------+
| id | fname | lname | store_id | department_id |
+----+-------+-------+----------+---------------+
|  4 | Jim   | Smith |        2 |             4 |
| 11 | Jill  | Stone |        1 |             4 |
+----+-------+-------+----------+---------------+
2 rows in set (0.00 sec)

mysql> SELECT id, CONCAT(fname, ' ', lname) AS name
 ->     FROM employees PARTITION (p0) ORDER BY lname;
+----+----------------+
| id | name           |
+----+----------------+
|  3 | Ellen Johnson  |
|  4 | Jim Smith      |
|  1 | Bob Taylor     |
|  2 | Frank Williams |
+----+----------------+
4 rows in set (0.06 sec)

mysql> SELECT store_id, COUNT(department_id) AS c
 ->     FROM employees PARTITION (p1,p2,p3)
 ->     GROUP BY store_id HAVING c > 4;
+---+----------+
| c | store_id |
+---+----------+
| 5 |        2 |
| 5 |        3 |
+---+----------+
2 rows in set (0.00 sec)

使用分区选择的语句可以与使用任何支持的分区类型的表一起使用。当使用[LINEAR] HASH[LINEAR] KEY分区创建表时,如果未指定分区的名称,MySQL 会自动将分区命名为p0p1p2、...、p*N-1*,其中*N是分区的数量。对于未明确命名的子分区,MySQL 会自动为每个分区p*X*中的子分区分配名称p*X*sp0p*X*sp1p*X*sp2、...、p*X*sp*M-1*,其中M*是子分区的数量。在针对这个表执行SELECT(或其他允许显式分区选择的 SQL 语句)时,您可以在PARTITION选项中使用这些生成的名称,如下所示:

mysql> CREATE TABLE employees_sub  (
 ->     id INT NOT NULL AUTO_INCREMENT,
 ->     fname VARCHAR(25) NOT NULL,
 ->     lname VARCHAR(25) NOT NULL,
 ->     store_id INT NOT NULL,
 ->     department_id INT NOT NULL,
 ->     PRIMARY KEY pk (id, lname)
 -> )
 ->     PARTITION BY RANGE(id)
 ->     SUBPARTITION BY KEY (lname)
 ->     SUBPARTITIONS 2 (
 ->         PARTITION p0 VALUES LESS THAN (5),
 ->         PARTITION p1 VALUES LESS THAN (10),
 ->         PARTITION p2 VALUES LESS THAN (15),
 ->         PARTITION p3 VALUES LESS THAN MAXVALUE
 -> );
Query OK, 0 rows affected (1.14 sec)

mysql> INSERT INTO employees_sub   # reuse data in employees table
 ->     SELECT * FROM employees;
Query OK, 18 rows affected (0.09 sec)
Records: 18  Duplicates: 0  Warnings: 0

mysql> SELECT id, CONCAT(fname, ' ', lname) AS name
 ->     FROM employees_sub PARTITION (p2sp1);
+----+---------------+
| id | name          |
+----+---------------+
| 10 | Lou Waters    |
| 14 | Fred Goldberg |
+----+---------------+
2 rows in set (0.00 sec)

您还可以在SELECT部分的INSERT ... SELECT语句中使用PARTITION选项,如下所示:

mysql> CREATE TABLE employees_copy LIKE employees;
Query OK, 0 rows affected (0.28 sec)

mysql> INSERT INTO employees_copy
 ->     SELECT * FROM employees PARTITION (p2);
Query OK, 5 rows affected (0.04 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM employees_copy;
+----+--------+----------+----------+---------------+
| id | fname  | lname    | store_id | department_id |
+----+--------+----------+----------+---------------+
| 10 | Lou    | Waters   |        2 |             4 |
| 11 | Jill   | Stone    |        1 |             4 |
| 12 | Roger  | White    |        3 |             2 |
| 13 | Howard | Andrews  |        1 |             2 |
| 14 | Fred   | Goldberg |        3 |             3 |
+----+--------+----------+----------+---------------+
5 rows in set (0.00 sec)

分区选择也可以与连接一起使用。假设我们使用以下语句创建和填充了两个表:

CREATE TABLE stores (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    city VARCHAR(30) NOT NULL
)
    PARTITION BY HASH(id)
    PARTITIONS 2;

INSERT INTO stores VALUES
    ('', 'Nambucca'), ('', 'Uranga'),
    ('', 'Bellingen'), ('', 'Grafton');

CREATE TABLE departments  (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(30) NOT NULL
)
    PARTITION BY KEY(id)
    PARTITIONS 2;

INSERT INTO departments VALUES
    ('', 'Sales'), ('', 'Customer Service'),
    ('', 'Delivery'), ('', 'Accounting');

您可以明确地从连接中的任何或所有表中选择分区(或子分区,或两者)。(用于从给定表中选择分区的PARTITION选项紧随表名之后,位于所有其他选项之前,包括任何表别名。)例如,以下查询获取所有在销售或交付部门(departments表的分区p1)中的商店(stores表的分区p0)中工作的员工的姓名、员工 ID、部门和城市:

mysql> SELECT
 ->     e.id AS 'Employee ID', CONCAT(e.fname, ' ', e.lname) AS Name,
 ->     s.city AS City, d.name AS department
 -> FROM employees AS e
 ->     JOIN stores PARTITION (p1) AS s ON e.store_id=s.id
 ->     JOIN departments PARTITION (p0) AS d ON e.department_id=d.id
 -> ORDER BY e.lname;
+-------------+---------------+-----------+------------+
| Employee ID | Name          | City      | department |
+-------------+---------------+-----------+------------+
|          14 | Fred Goldberg | Bellingen | Delivery   |
|           5 | Mary Jones    | Nambucca  | Sales      |
|          17 | Mark Morgan   | Bellingen | Delivery   |
|           9 | Andy Smith    | Nambucca  | Delivery   |
|           8 | June Wilson   | Bellingen | Sales      |
+-------------+---------------+-----------+------------+
5 rows in set (0.00 sec)

有关 MySQL 中连接的一般信息,请参见第 15.2.13.2 节,“JOIN Clause”。

PARTITION选项与DELETE语句一起使用时,只有在选项中列出的那些分区(和子分区,如果有)会被检查以删除行。任何其他分区都会被忽略,如下所示:

mysql> SELECT * FROM employees WHERE fname LIKE 'j%';
+----+-------+--------+----------+---------------+
| id | fname | lname  | store_id | department_id |
+----+-------+--------+----------+---------------+
|  4 | Jim   | Smith  |        2 |             4 |
|  8 | June  | Wilson |        3 |             1 |
| 11 | Jill  | Stone  |        1 |             4 |
+----+-------+--------+----------+---------------+
3 rows in set (0.00 sec)

mysql> DELETE FROM employees PARTITION (p0, p1)
 ->     WHERE fname LIKE 'j%';
Query OK, 2 rows affected (0.09 sec)

mysql> SELECT * FROM employees WHERE fname LIKE 'j%';
+----+-------+-------+----------+---------------+
| id | fname | lname | store_id | department_id |
+----+-------+-------+----------+---------------+
| 11 | Jill  | Stone |        1 |             4 |
+----+-------+-------+----------+---------------+
1 row in set (0.00 sec)

只有与WHERE条件匹配的p0p1分区中的两行被删除。从第二次运行SELECT时的结果中可以看到,表中仍然存在一行与WHERE条件匹配,但位于不同的分区(p2)。

使用显式分区选择的UPDATE语句的行为相同;只有在确定要更新的行时,才考虑PARTITION选项引用的分区中的行,可以通过执行以下语句来查看:

mysql> UPDATE employees PARTITION (p0) 
 ->     SET store_id = 2 WHERE fname = 'Jill';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0

mysql> SELECT * FROM employees WHERE fname = 'Jill';
+----+-------+-------+----------+---------------+
| id | fname | lname | store_id | department_id |
+----+-------+-------+----------+---------------+
| 11 | Jill  | Stone |        1 |             4 |
+----+-------+-------+----------+---------------+
1 row in set (0.00 sec)

mysql> UPDATE employees PARTITION (p2)
 ->     SET store_id = 2 WHERE fname = 'Jill';
Query OK, 1 row affected (0.09 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM employees WHERE fname = 'Jill';
+----+-------+-------+----------+---------------+
| id | fname | lname | store_id | department_id |
+----+-------+-------+----------+---------------+
| 11 | Jill  | Stone |        2 |             4 |
+----+-------+-------+----------+---------------+
1 row in set (0.00 sec)

同样,当与DELETE一起使用PARTITION时,只有在分区列表中命名的分区中的行才会被检查删除。

对于插入行的语句,行为有所不同,找不到合适分区会导致语句失败。这对于INSERTREPLACE语句都是适用的,如下所示:

mysql> INSERT INTO employees PARTITION (p2) VALUES (20, 'Jan', 'Jones', 1, 3);
ERROR 1729 (HY000): Found a row not matching the given partition set
mysql> INSERT INTO employees PARTITION (p3) VALUES (20, 'Jan', 'Jones', 1, 3);
Query OK, 1 row affected (0.07 sec)

mysql> REPLACE INTO employees PARTITION (p0) VALUES (20, 'Jan', 'Jones', 3, 2);
ERROR 1729 (HY000): Found a row not matching the given partition set 
mysql> REPLACE INTO employees PARTITION (p3) VALUES (20, 'Jan', 'Jones', 3, 2);
Query OK, 2 rows affected (0.09 sec)

对于使用InnoDB存储引擎的分区表写入多行的语句:如果VALUES后面的列表中的任何行无法写入到*partition_names*列表中指定的分区之一,则整个语句将失败,不会写入任何行。这在下面的示例中展示了INSERT语句,重用了之前创建的employees表:

mysql> ALTER TABLE employees
 ->     REORGANIZE PARTITION p3 INTO (
 ->         PARTITION p3 VALUES LESS THAN (20),
 ->         PARTITION p4 VALUES LESS THAN (25),
 ->         PARTITION p5 VALUES LESS THAN MAXVALUE
 ->     );
Query OK, 6 rows affected (2.09 sec)
Records: 6  Duplicates: 0  Warnings: 0

mysql> SHOW CREATE TABLE employees\G
*************************** 1\. row ***************************
       Table: employees
Create Table: CREATE TABLE `employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `fname` varchar(25) NOT NULL,
  `lname` varchar(25) NOT NULL,
  `store_id` int(11) NOT NULL,
  `department_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4
/*!50100 PARTITION BY RANGE (id)
(PARTITION p0 VALUES LESS THAN (5) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (10) ENGINE = InnoDB,
 PARTITION p2 VALUES LESS THAN (15) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (20) ENGINE = InnoDB,
 PARTITION p4 VALUES LESS THAN (25) ENGINE = InnoDB,
 PARTITION p5 VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */ 1 row in set (0.00 sec)

mysql> INSERT INTO employees PARTITION (p3, p4) VALUES
 ->     (24, 'Tim', 'Greene', 3, 1),  (26, 'Linda', 'Mills', 2, 1);
ERROR 1729 (HY000): Found a row not matching the given partition set 
mysql> INSERT INTO employees PARTITION (p3, p4, p5) VALUES
 ->     (24, 'Tim', 'Greene', 3, 1),  (26, 'Linda', 'Mills', 2, 1);
Query OK, 2 rows affected (0.06 sec)
Records: 2  Duplicates: 0  Warnings: 0

对于写入多行的INSERT语句和REPLACE语句,上述内容都是适用的。

对于使用提供自动分区的存储引擎(如NDB)的表,分区选择被禁用。

26.6 关于分区的限制和限制

原文:dev.mysql.com/doc/refman/8.0/en/partitioning-limitations.html

26.6.1 分区键、主键和唯一键

26.6.2 与存储引擎相关的分区限制

26.6.3 与函数相关的分区限制

本节讨论了 MySQL 分区支持的当前限制和限制。

禁止的结构。 不允许在分区表达式中使用以下结构:

  • 存储过程、存储函数、可加载函数或插件。

  • 声明变量或用户变量。

有关允许在分区表达式中使用的 SQL 函数列表,请参见 第 26.6.3 节,“与函数相关的分区限制”。

算术和逻辑运算符。 在分区表达式中允许使用算术运算符 +, -, 和 *。然而,结果必须是整数值或 NULL(除了 [LINEAR] KEY 分区,如本章其他地方讨论的那样;有关更多信息,请参见 第 26.2 节,“分区类型”)。

DIV 运算符也受支持;不允许使用 / 运算符。

位运算符 |, &, ^, <<, >>, 和 ~ 不允许在分区表达式中使用。

服务器 SQL 模式。 使用自定义分区的表不会保留创建时有效的 SQL 模式。如本手册其他地方所述(请参见 第 7.1.11 节,“服务器 SQL 模式”),许多 MySQL 函数和运算符的结果可能根据服务器 SQL 模式而变化。因此,在创建分区表后任何时候更改 SQL 模式可能导致这些表行为发生重大变化,并可能轻易导致数据损坏或丢失。因此,强烈建议您在创建分区表后永远不要更改服务器 SQL 模式

对于服务器 SQL 模式中的一种更改使分区表无法使用的情况,请考虑以下 CREATE TABLE 语句,只有在 NO_UNSIGNED_SUBTRACTION 模式生效时才能成功执行:

mysql> SELECT @@sql_mode;
+------------+
| @@sql_mode |
+------------+
|            |
+------------+
1 row in set (0.00 sec)

mysql> CREATE TABLE tu (c1 BIGINT UNSIGNED)
 ->   PARTITION BY RANGE(c1 - 10) (
 ->     PARTITION p0 VALUES LESS THAN (-5),
 ->     PARTITION p1 VALUES LESS THAN (0),
 ->     PARTITION p2 VALUES LESS THAN (5),
 ->     PARTITION p3 VALUES LESS THAN (10),
 ->     PARTITION p4 VALUES LESS THAN (MAXVALUE)
 -> );
ERROR 1563 (HY000): Partition constant is out of partition function domain 
mysql> SET sql_mode='NO_UNSIGNED_SUBTRACTION';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@sql_mode;
+-------------------------+
| @@sql_mode              |
+-------------------------+
| NO_UNSIGNED_SUBTRACTION |
+-------------------------+
1 row in set (0.00 sec)

mysql> CREATE TABLE tu (c1 BIGINT UNSIGNED)
 ->   PARTITION BY RANGE(c1 - 10) (
 ->     PARTITION p0 VALUES LESS THAN (-5),
 ->     PARTITION p1 VALUES LESS THAN (0),
 ->     PARTITION p2 VALUES LESS THAN (5),
 ->     PARTITION p3 VALUES LESS THAN (10),
 ->     PARTITION p4 VALUES LESS THAN (MAXVALUE)
 -> );
Query OK, 0 rows affected (0.05 sec)

如果在创建 tu 后移除 NO_UNSIGNED_SUBTRACTION 服务器 SQL 模式,则可能无法再访问此表:

mysql> SET sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM tu;
ERROR 1563 (HY000): Partition constant is out of partition function domain
mysql> INSERT INTO tu VALUES (20);
ERROR 1563 (HY000): Partition constant is out of partition function domain

另请参阅 第 7.1.11 节,“服务器 SQL 模式”。

服务器 SQL 模式也会影响分区表的复制。源和副本上不同的 SQL 模式可能导致分区表达式的评估不同;这可能导致数据在源和副本的给定表的副本之间分布不同,并且甚至可能导致在源上成功的分区表插入在副本上失败。为获得最佳结果,您应始终在源和副本上使用相同的服务器 SQL 模式。

性能考虑。 分区操作对性能的一些影响如下:

  • 文件系统操作。 分区和重新分区操作(例如ALTER TABLE 使用 PARTITION BY ..., REORGANIZE PARTITION, 或 REMOVE PARTITIONING)的实现取决于文件系统操作。这意味着这些操作的速度受到诸如文件系统类型和特性、磁盘速度、交换空间、操作系统的文件处理效率以及与文件处理相关的 MySQL 服务器选项和变量的影响。特别是,您应确保 large_files_support 已启用,并且 open_files_limit 已正确设置。涉及 InnoDB 表的分区和重新分区操作可能通过启用 innodb_file_per_table 变得更加高效。

    另请参阅 最大分区数。

  • 表锁。 通常,对表执行分区操作的进程会对表进行写锁定。对这些表的读取相对不受影响;挂起的 INSERTUPDATE 操作将在分区操作完成后立即执行。有关 InnoDB 特定的例外情况,请参阅 分区操作。

  • 索引;分区修剪。 与非分区表一样,正确使用索引可以显着加快对分区表的查询速度。此外,设计分区表和对这些表的查询以利用分区修剪可以显著提高性能。有关更多信息,请参阅第 26.4 节,“分区修剪”。

    支持对分区表进行索引条件下推。请参阅第 10.2.1.6 节,“索引条件下推优化”。

  • 使用 LOAD DATA 的性能。 在 MySQL 8.0 中,LOAD DATA使用缓冲区来提高性能。您应该知道,为了实现这一点,每个分区的缓冲区使用 130 KB 内存。

最大分区数。 对于不使用NDB存储引擎的给定表,最大可能的分区数为 8192。此数字包括子分区。

对于使用NDB存储引擎的表的最大可能的用户定义分区数根据使用的 NDB Cluster 软件版本、数据节点数量和其他因素确定。有关更多信息,请参阅 NDB 和用户定义分区。

如果在创建具有大量分区的表(但少于最大值)时遇到错误消息,例如从存储引擎获取错误...:打开文件时资源不足,您可以通过增加open_files_limit系统变量的值来解决此问题。但是,这取决于操作系统,并且在所有平台上可能不可行或不可取;有关更多信息,请参阅第 B.3.2.16 节,“文件未找到和类似错误”。在某些情况下,由于其他原因,使用大量(数百个)分区也可能不可取,因此使用更多分区并不会自动导致更好的结果。

另请参阅文件系统操作。

不支持对分区 InnoDB 表使用外键。 使用InnoDB存储引擎的分区表不支持外键。更具体地说,这意味着以下两个语句是正确的:

  1. 不得包含外键引用的InnoDB表的定义使用用户定义的分区;包含外键引用的InnoDB表的定义不得分区。

  2. 任何InnoDB表定义都不能包含对用户分区表的外键引用;任何具有用户定义分区的InnoDB表都不能包含被外键引用的列。

刚刚列出的限制范围包括所有使用InnoDB存储引擎的表。不允许创建CREATE TABLEALTER TABLE语句导致违反这些限制的表。

ALTER TABLE ... ORDER BY。 对分区表运行的ALTER TABLE ... ORDER BY *column*语句仅导致每个分区内的行排序。

ADD COLUMN ... ALGORITHM=INSTANT。 一旦在分区表上执行ALTER TABLE ... ADD COLUMN ... ALGORITHM=INSTANT,就不再可能与该表交换分区。

通过修改主键对 REPLACE 语句的影响。 在某些情况下可能是可取的(参见第 26.6.1 节,“分区键、主键和唯一键”),修改表的主键。请注意,如果您的应用程序使用REPLACE语句并且您这样做,这些语句的结果可能会发生 drastical 改变。有关更多信息和示例,请参见第 15.2.12 节,“REPLACE 语句”。

全文索引。 分区表不支持FULLTEXT索引或搜索。

空间列。 具有空间数据类型(如POINTGEOMETRY)的列不能在分区表中使用。

临时表。 临时表不能被分区。

日志表。 无法对日志表进行分区;对这样的表运行ALTER TABLE ... PARTITION BY ...语句会失败并显示错误。

分区键的数据类型。 分区键必须是整数列或解析为整数的表达式。不能使用包含ENUM列的表达式。列或表达式的值也可以是NULL;请参见第 26.2.7 节,“MySQL 分区如何处理 NULL”。

这个限制有两个例外:

  1. 当通过[LINEAR] KEY进行分区时,可以使用除TEXTBLOB之外的任何有效的 MySQL 数据类型作为分区键,因为内部键哈希函数会从这些类型中生成正确的数据类型。例如,以下两个CREATE TABLE语句是有效的:

    CREATE TABLE tkc (c1 CHAR)
    PARTITION BY KEY(c1)
    PARTITIONS 4;
    
    CREATE TABLE tke
        ( c1 ENUM('red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet') )
    PARTITION BY LINEAR KEY(c1)
    PARTITIONS 6;
    
  2. 当使用RANGE COLUMNSLIST COLUMNS进行分区时,可以使用字符串、DATEDATETIME列。例如,以下每个CREATE TABLE语句都是有效的:

    CREATE TABLE rc (c1 INT, c2 DATE)
    PARTITION BY RANGE COLUMNS(c2) (
        PARTITION p0 VALUES LESS THAN('1990-01-01'),
        PARTITION p1 VALUES LESS THAN('1995-01-01'),
        PARTITION p2 VALUES LESS THAN('2000-01-01'),
        PARTITION p3 VALUES LESS THAN('2005-01-01'),
        PARTITION p4 VALUES LESS THAN(MAXVALUE)
    );
    
    CREATE TABLE lc (c1 INT, c2 CHAR(1))
    PARTITION BY LIST COLUMNS(c2) (
        PARTITION p0 VALUES IN('a', 'd', 'g', 'j', 'm', 'p', 's', 'v', 'y'),
        PARTITION p1 VALUES IN('b', 'e', 'h', 'k', 'n', 'q', 't', 'w', 'z'),
        PARTITION p2 VALUES IN('c', 'f', 'i', 'l', 'o', 'r', 'u', 'x', NULL)
    );
    

前述两个例外情况均不适用于BLOBTEXT列类型。

子查询。 分区键不能是子查询,即使该子查询解析为整数值或NULL

不支持列索引前缀用于键分区。 在创建按键分区的表时,分区键中使用列前缀的任何列都不会用于表的分区函数。考虑以下CREATE TABLE语句,其中有三个VARCHAR列,主键使用了所有三列,并为其中两列指定了前缀:

CREATE TABLE t1 (
    a VARCHAR(10000),
    b VARCHAR(25),
    c VARCHAR(10),
    PRIMARY KEY (a(10), b, c(2))
) PARTITION BY KEY() PARTITIONS 2;

此语句被接受,但实际创建的表实际上是如同您发出了以下语句一样创建的,仅使用了不包含前缀的主键列(列b)作为分区键:

CREATE TABLE t1 (
    a VARCHAR(10000),
    b VARCHAR(25),
    c VARCHAR(10),
    PRIMARY KEY (a(10), b, c(2))
) PARTITION BY KEY(b) PARTITIONS 2;

在 MySQL 8.0.21 之前,没有发出警告或提供任何其他指示表明发生了这种情况,除非所有指定为分区键的列都使用了前缀,此时语句会失败,但会显示一个误导性的错误消息,如下所示:

mysql> CREATE TABLE t2 (
 ->     a VARCHAR(10000),
 ->     b VARCHAR(25),
 ->     c VARCHAR(10),
 ->     PRIMARY KEY (a(10), b(5), c(2))
 -> ) PARTITION BY KEY() PARTITIONS 2;
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the
table's partitioning function

当执行ALTER TABLE或升级此类表时也会发生这种情况。

从 MySQL 8.0.21 开始,不再支持这种宽松行为(并且可能在将来的 MySQL 版本中删除)。从 MySQL 8.0.21 开始,使用一个或多个具有前缀的列作为分区键会对每个这样的列产生警告,如下所示:

mysql> CREATE TABLE t1 (
 ->     a VARCHAR(10000),
 ->     b VARCHAR(25),
 ->     c VARCHAR(10),
 ->     PRIMARY KEY (a(10), b, c(2))
 -> ) PARTITION BY KEY() PARTITIONS 2;
Query OK, 0 rows affected, 2 warnings (1.25 sec)

mysql> SHOW WARNINGS\G
*************************** 1\. row ***************************
  Level: Warning
   Code: 1681
Message: Column 'test.t1.a' having prefix key part 'a(10)' is ignored by the
partitioning function. Use of prefixed columns in the PARTITION BY KEY() clause
is deprecated and will be removed in a future release.
*************************** 2\. row ***************************
  Level: Warning
   Code: 1681
Message: Column 'test.t1.c' having prefix key part 'c(2)' is ignored by the
partitioning function. Use of prefixed columns in the PARTITION BY KEY() clause
is deprecated and will be removed in a future release. 2 rows in set (0.00 sec)

这包括分区函数中使用的列被隐式定义为通过使用空的PARTITION BY KEY()子句来定义表的主键中的列的情况。

在 MySQL 8.0.21 及更高版本中,如果分区键指定的所有列都使用了前缀,则使用的CREATE TABLE语句将因错误消息而失败,该消息正确标识了问题:

mysql> CREATE TABLE t1 (
 ->     a VARCHAR(10000),
 ->     b VARCHAR(25),
 ->     c VARCHAR(10),
 ->     PRIMARY KEY (a(10), b(5), c(2))
 -> ) PARTITION BY KEY() PARTITIONS 2;
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's
partitioning function (prefixed columns are not considered).

有关按键分区表的一般信息,请参见第 26.2.5 节,“键分区”。

子分区存在问题。 子分区必须使用HASHKEY分区。只有RANGELIST分区可以进行子分区;HASHKEY分区不能进行子分区。

SUBPARTITION BY KEY要求明确指定子分区列或列,不同于PARTITION BY KEY的情况,后者可以省略(在这种情况下,默认使用表的主键列)。考虑通过此语句创建的表:

CREATE TABLE ts (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(30)
);

您可以通过类似于以下语句创建一个具有相同列、按KEY分区的表:

CREATE TABLE ts (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(30)
)
PARTITION BY KEY()
PARTITIONS 4;

前面的语句被视为如下所示编写,其中表的主键列用作分区列:

CREATE TABLE ts (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(30)
)
PARTITION BY KEY(id)
PARTITIONS 4;

然而,尝试使用默认列作为子分区列创建子分区表的以下语句将失败,必须为语句指定列才能成功,如下所示:

mysql> CREATE TABLE ts (
 ->     id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 ->     name VARCHAR(30)
 -> )
 -> PARTITION BY RANGE(id)
 -> SUBPARTITION BY KEY()
 -> SUBPARTITIONS 4
 -> (
 ->     PARTITION p0 VALUES LESS THAN (100),
 ->     PARTITION p1 VALUES LESS THAN (MAXVALUE)
 -> );
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use near ') 
mysql> CREATE TABLE ts (
 ->     id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 ->     name VARCHAR(30)
 -> )
 -> PARTITION BY RANGE(id)
 -> SUBPARTITION BY KEY(id)
 -> SUBPARTITIONS 4
 -> (
 ->     PARTITION p0 VALUES LESS THAN (100),
 ->     PARTITION p1 VALUES LESS THAN (MAXVALUE)
 -> );
Query OK, 0 rows affected (0.07 sec)

这是一个已知问题(参见 Bug #51470)。

数据目录和索引目录选项。 忽略表级DATA DIRECTORYINDEX DIRECTORY选项(参见 Bug #32091)。您可以为InnoDB表的各个分区或子分区使用这些选项。截至 MySQL 8.0.21,DATA DIRECTORY子句中指定的目录必须为InnoDB所知。有关更多信息,请参见使用 DATA DIRECTORY 子句。

修复和重建分区表。 CHECK TABLEOPTIMIZE TABLEANALYZE TABLEREPAIR TABLE语句支持分区表。

此外,您可以使用ALTER TABLE ... REBUILD PARTITION来重建分区表的一个或多个分区;ALTER TABLE ... REORGANIZE PARTITION也会导致分区重建。有关这两个语句的更多信息,请参见第 15.1.9 节,“ALTER TABLE Statement”。

ANALYZECHECKOPTIMIZEREPAIRTRUNCATE操作支持子分区。请参见第 15.1.9.1 节,“ALTER TABLE Partition Operations”。

用于分区和子分区的文件名分隔符。 表分区和子分区文件名包括生成的分隔符,如#P##SP#。此类分隔符的大小写可能有所不同,不应依赖于此。

26.6.1 分区键、主键和唯一键

原文:dev.mysql.com/doc/refman/8.0/en/partitioning-limitations-partitioning-keys-unique-keys.html

本节讨论了分区键与主键和唯一键的关系。规定这种关系的规则可以表述如下:用于分区表的分区表达式中的所有列必须是表可能具有的每个唯一键的一部分。

换句话说,表上的每个唯一键都必须使用表的分区表达式中的每一列。(这也包括表的主键,因为根据定义,它是一个唯一键。这种特殊情况稍后在本节中讨论。)例如,以下每个表创建语句都是无效的:

CREATE TABLE t1 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col2)
)
PARTITION BY HASH(col3)
PARTITIONS 4;

CREATE TABLE t2 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1),
    UNIQUE KEY (col3)
)
PARTITION BY HASH(col1 + col3)
PARTITIONS 4;

在每种情况下,建议的表至少应该有一个不包括分区表达式中使用的所有列的唯一键。

每个以下语句都是有效的,并代表了相应的无效表创建语句可以正常工作的一种方式:

CREATE TABLE t1 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col2, col3)
)
PARTITION BY HASH(col3)
PARTITIONS 4;

CREATE TABLE t2 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col3)
)
PARTITION BY HASH(col1 + col3)
PARTITIONS 4;

这个例子展示了这种情况下产生的错误:

mysql> CREATE TABLE t3 (
 ->     col1 INT NOT NULL,
 ->     col2 DATE NOT NULL,
 ->     col3 INT NOT NULL,
 ->     col4 INT NOT NULL,
 ->     UNIQUE KEY (col1, col2),
 ->     UNIQUE KEY (col3)
 -> )
 -> PARTITION BY HASH(col1 + col3)
 -> PARTITIONS 4;
ERROR 1491 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function

CREATE TABLE 语句失败,因为col1col3都包含在建议的分区键中,但这两列都不是表上的两个唯一键的一部分。这显示了无效表定义的一种可能修复方法:

mysql> CREATE TABLE t3 (
 ->     col1 INT NOT NULL,
 ->     col2 DATE NOT NULL,
 ->     col3 INT NOT NULL,
 ->     col4 INT NOT NULL,
 ->     UNIQUE KEY (col1, col2, col3),
 ->     UNIQUE KEY (col3)
 -> )
 -> PARTITION BY HASH(col3)
 -> PARTITIONS 4;
Query OK, 0 rows affected (0.05 sec)

在这种情况下,建议的分区键col3是两个唯一键的一部分,表创建语句成功。

以下表根本无法分区,因为无法在分区键中包含任何同时属于唯一键的列:

CREATE TABLE t4 (
    col1 INT NOT NULL,
    col2 INT NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col3),
    UNIQUE KEY (col2, col4)
);

由于每个主键根据定义都是唯一键,如果表有主键,这个限制也包括表的主键。例如,下面的两个语句是无效的:

CREATE TABLE t5 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    PRIMARY KEY(col1, col2)
)
PARTITION BY HASH(col3)
PARTITIONS 4;

CREATE TABLE t6 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    PRIMARY KEY(col1, col3),
    UNIQUE KEY(col2)
)
PARTITION BY HASH( YEAR(col2) )
PARTITIONS 4;

在这两种情况下,主键都不包括分区表达式中引用的所有列。然而,下面的两个语句都是有效的:

CREATE TABLE t7 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    PRIMARY KEY(col1, col2)
)
PARTITION BY HASH(col1 + YEAR(col2))
PARTITIONS 4;

CREATE TABLE t8 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    PRIMARY KEY(col1, col2, col4),
    UNIQUE KEY(col2, col1)
)
PARTITION BY HASH(col1 + YEAR(col2))
PARTITIONS 4;

如果一个表没有唯一键——这包括没有主键——那么这个限制就不适用,你可以使用分区表达式中的任何列或多列,只要列的类型与分区类型兼容。

出于同样的原因,除非该键包含表的分区表达式中使用的所有列,否则您不能随后向分区表添加唯一键。考虑在此处创建的分区表:

mysql> CREATE TABLE t_no_pk (c1 INT, c2 INT)
 ->     PARTITION BY RANGE(c1) (
 ->         PARTITION p0 VALUES LESS THAN (10),
 ->         PARTITION p1 VALUES LESS THAN (20),
 ->         PARTITION p2 VALUES LESS THAN (30),
 ->         PARTITION p3 VALUES LESS THAN (40)
 ->     );
Query OK, 0 rows affected (0.12 sec)

可以使用以下任一ALTER TABLE语句向t_no_pk添加主键:

#  possible PK
mysql> ALTER TABLE t_no_pk ADD PRIMARY KEY(c1);
Query OK, 0 rows affected (0.13 sec)
Records: 0  Duplicates: 0  Warnings: 0

# drop this PK
mysql> ALTER TABLE t_no_pk DROP PRIMARY KEY;
Query OK, 0 rows affected (0.10 sec)
Records: 0  Duplicates: 0  Warnings: 0

#  use another possible PK
mysql> ALTER TABLE t_no_pk ADD PRIMARY KEY(c1, c2);
Query OK, 0 rows affected (0.12 sec)
Records: 0  Duplicates: 0  Warnings: 0

# drop this PK
mysql> ALTER TABLE t_no_pk DROP PRIMARY KEY;
Query OK, 0 rows affected (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 0

然而,下一个语句失败,因为c1是分区键的一部分,但不是建议的主键的一部分:

#  fails with error 1503
mysql> ALTER TABLE t_no_pk ADD PRIMARY KEY(c2);
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function

由于t_no_pk只有c1在其分区表达式中,尝试仅在c2上添加唯一键将失败。但是,您可以添加一个使用c1c2的唯一键。

这些规则也适用于您希望使用ALTER TABLE ... PARTITION BY对现有的非分区表进行分区的情况。考虑一个如下所示创建的表np_pk

mysql> CREATE TABLE np_pk (
 ->     id INT NOT NULL AUTO_INCREMENT,
 ->     name VARCHAR(50),
 ->     added DATE,
 ->     PRIMARY KEY (id)
 -> );
Query OK, 0 rows affected (0.08 sec)

以下ALTER TABLE语句将因为added列不是表中任何唯一键的一部分而失败:

mysql> ALTER TABLE np_pk
 ->     PARTITION BY HASH( TO_DAYS(added) )
 ->     PARTITIONS 4;
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function

然而,使用id列作为分区列的语句是有效的,如下所示:

mysql> ALTER TABLE np_pk
 ->     PARTITION BY HASH(id)
 ->     PARTITIONS 4;
Query OK, 0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 0

对于np_pk,唯一可以用作分区表达式的列是id;如果您希望使用分区表达式中的任何其他列对该表进行分区,您必须首先修改表,可以通过将所需的列添加到主键中或完全删除主键。

26.6.2 关于存储引擎的分区限制

译文链接:dev.mysql.com/doc/refman/8.0/en/partitioning-limitations-storage-engines.html

在 MySQL 8.0 中,分区支持实际上并非由 MySQL Server 提供,而是由表存储引擎自身或本地分区处理程序提供。在 MySQL 8.0 中,只有InnoDBNDB存储引擎提供本地分区处理程序。这意味着分区表不能使用除这些之外的任何其他存储引擎来创建。(您必须使用带有NDB存储引擎的 MySQL NDB Cluster 来创建NDB表。)

InnoDB 存储引擎。 InnoDB外键和 MySQL 分区不兼容。分区的InnoDB表不能具有外键引用,也不能具有被外键引用的列。具有或被外键引用的InnoDB表不能进行分区。

对于使用InnoDB的分区表,ALTER TABLE ... OPTIMIZE PARTITION无法正确工作。对于这些表,请改用ALTER TABLE ... REBUILD PARTITIONALTER TABLE ... ANALYZE PARTITION。有关更多信息,请参见第 15.1.9.1 节,“ALTER TABLE Partition Operations”。

用户定义的分区和 NDB 存储引擎(NDB Cluster)。 KEY(包括LINEAR KEY)分区是唯一支持的NDB存储引擎的分区类型。在 NDB Cluster 中,通常情况下不可能使用除[LINEAR] KEY之外的任何分区类型来创建 NDB Cluster 表,尝试这样做会导致错误。

异常(不适用于生产):可以通过在 NDB Cluster SQL 节点上设置new系统变量为ON来覆盖此限制。如果您选择这样做,您应该意识到不支持使用除[LINEAR] KEY之外的其他分区类型的表进行生产。*在这种情况下,您可以创建和使用除KEYLINEAR KEY之外的其他分区类型的表,但这完全是自担风险的。*您还应该意识到,此功能现已被弃用,并可能在将来的 NDB Cluster 版本中不经通知地被移除。

可以为NDB 表定义的最大分区数取决于集群中的数据节点和节点组数量、正在使用的 NDB Cluster 软件版本以及其他因素。有关更多信息,请参见 NDB 和用户定义的分区。

NDB表中每个分区中可以存储的固定大小数据的最大量为 128 TB。以前,这个值是 16 GB。

会导致用户分区的NDB 表不满足以下两个要求之一或两者的CREATE TABLEALTER TABLE语句不被允许,并且会因错误而失败:

  1. 表必须具有显式主键。

  2. 表的分区表达式中列出的所有列必须是主键的一部分。

异常。 如果使用空列列表(即使用PARTITION BY KEY()PARTITION BY LINEAR KEY())创建用户分区的NDB 表,则不需要显式主键。

分区选择。 NDB 表不支持分区选择。有关更多信息,请参见第 26.5 节“分区选择”。

升级分区表。 在执行升级时,使用KEY进行分区的表必须进行转储和重新加载。使用除InnoDB之外的存储引擎进行分区的表无法从 MySQL 5.7 或更早版本升级到 MySQL 8.0 或更高版本;您必须在升级之前使用ALTER TABLE ... REMOVE PARTITIONING删除这些表的分区,或者使用ALTER TABLE ... ENGINE=INNODB将其转换为InnoDB

有关将MyISAM表转换为InnoDB的信息,请参见第 17.6.1.5 节“从 MyISAM 转换表到 InnoDB”。

26.6.3 与函数相关的分区限制

原文:dev.mysql.com/doc/refman/8.0/en/partitioning-limitations-functions.html

这一部分讨论了 MySQL 分区中与分区表达式中使用的函数相关的限制。

只有以下列出的 MySQL 函数允许在分区表达式中使用:

  • ABS()

  • CEILING()(参见 CEILING() 和 FLOOR() and FLOOR()"))

  • DATEDIFF()

  • DAY()

  • DAYOFMONTH()

  • DAYOFWEEK()

  • DAYOFYEAR()

  • EXTRACT()(参见 带有 WEEK 参数的 EXTRACT() 函数 function with WEEK specifier"))

  • FLOOR()(参见 CEILING() 和 FLOOR() and FLOOR()"))

  • HOUR()

  • MICROSECOND()

  • MINUTE()

  • MOD()

  • MONTH()

  • QUARTER()

  • SECOND()

  • TIME_TO_SEC()

  • TO_DAYS()

  • TO_SECONDS()

  • UNIX_TIMESTAMP()(带有 TIMESTAMP 列)

  • WEEKDAY()

  • YEAR()

  • YEARWEEK()

在 MySQL 8.0 中,支持对 TO_DAYS()TO_SECONDS()YEAR()UNIX_TIMESTAMP() 函数进行分区修剪。更多信息请参见 第 26.4 节,“分区修剪”。

CEILING()和 FLOOR()。 如果这些函数的参数是精确数值类型,如INT类型或DECIMAL之一,则每个函数仅返回整数。这意味着,例如,以下CREATE TABLE语句将因错误而失败,如下所示:

mysql> CREATE TABLE t (c FLOAT) PARTITION BY LIST( FLOOR(c) )(
 ->     PARTITION p0 VALUES IN (1,3,5),
 ->     PARTITION p1 VALUES IN (2,4,6)
 -> );
ERROR 1490 (HY000): The PARTITION function returns the wrong type

带有 WEEK 指定符的 EXTRACT()函数。 当作为EXTRACT(WEEK FROM *col*)使用时,EXTRACT()函数返回的值取决于default_week_format系统变量的值。因此,当指定单位为WEEK时,EXTRACT()不允许作为分区函数。(Bug #54483)

查看第 14.6.2 节,“数学函数”,了解这些函数的返回类型的更多信息,以及第 13.1 节,“数值数据类型”。

第二十七章 存储对象

原文:dev.mysql.com/doc/refman/8.0/en/stored-objects.html

目录

27.1 定义存储程序

27.2 使用存储过程

27.2.1 存储过程语法

27.2.2 存储过程和 MySQL 权限

27.2.3 存储过程元数据

27.2.4 存储过程、函数、触发器和 LAST_INSERT_ID()

27.3 使用触发器

27.3.1 触发器语法和示例

27.3.2 触发器元数据

27.4 使用事件调度程序

27.4.1 事件调度程序概述

27.4.2 事件调度程序配置

27.4.3 事件语法

27.4.4 事件元数据

27.4.5 事件调度程序状态

27.4.6 事件调度程序和 MySQL 权限

27.5 使用视图

27.5.1 视图语法

27.5.2 视图处理算法

27.5.3 可更新和可插入视图

27.5.4 视图 WITH CHECK OPTION 子句

27.5.5 视图元数据

27.6 存储对象访问控制

27.7 存储程序二进制日志记录

27.8 存储程序限制

27.9 视图限制

本章讨论了以 SQL 代码形式定义并存储在服务器上以供以后执行的存储数据库对象。

存储对象包括以下对象类型:

  • 存储过程:使用CREATE PROCEDURE创建的对象,并使用CALL语句调用。存储过程没有返回值,但可以修改其参数以供调用者稍后检查。它还可以生成结果集以返回给客户端程序。

  • 存储函数:使用CREATE FUNCTION创建的对象,类似于内置函数。您可以在表达式中调用它,并在表达式评估期间返回一个值。

  • 触发器:使用CREATE TRIGGER创建的与表关联的对象。当表发生特定事件时(如插入或更新),触发器被激活。

  • 事件:使用CREATE EVENT创建的对象,并根据计划由服务器调用。

  • 视图:使用CREATE VIEW创建的对象,当引用时产生一个结果集。视图充当虚拟表。

本文档中使用的术语反映了存储对象层次结构:

  • 存储程序包括存储过程和函数。

  • 存储程序包括存储过程、触发器和事件。

  • 存储对象包括存储程序和视图。

本章描述了如何使用存储对象。以下各节提供了有关与这些对象相关的语句的 SQL 语法的附加信息,以及有关对象处理的信息:

  • 对于每种对象类型,都有CREATEALTERDROP语句来控制对象的存在和定义方式。参见第 15.1 节,“数据定义语句”。

  • CALL语句用于调用存储过程。参见第 15.2.1 节,“CALL 语句”。

  • 存储程序定义包括一个主体,可以使用复合语句、循环、条件语句和声明变量。参见第 15.6 节,“复合语句语法”。

  • 存储程序引用的对象的元数据更改会被检测到,并在下次执行程序时导致受影响语句的自动重新解析。有关更多信息,请参见第 10.10.3 节,“准备语句和存储程序的缓存”。

27.1 定义存储程序

原文:dev.mysql.com/doc/refman/8.0/en/stored-programs-defining.html

每个存储程序包含一个由 SQL 语句组成的主体。该语句可以是由分号(;)字符分隔的多个语句组成的复合语句。例如,以下存储过程具有由BEGIN ... END块组成的主体,其中包含一个SET语句和一个包含另一个SET语句的REPEAT循环:

CREATE PROCEDURE dorepeat(p1 INT)
BEGIN
  SET @x = 0;
  REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
END;

如果您使用mysql客户端程序定义包含分号字符的存储程序,则会出现问题。默认情况下,mysql本身将分号识别为语句分隔符,因此您必须临时重新定义分隔符,以使mysql将整个存储程序定义传递给服务器。

要重新定义mysql的分隔符,请使用delimiter命令。以下示例展示了如何为刚刚显示的dorepeat()过程执行此操作。将分隔符更改为//以便将整个定义作为单个语句传递给服务器,然后在调用过程之前将其恢复为;。这样可以使过程体中使用的;分隔符传递到服务器,而不是被mysql本身解释。

mysql> delimiter //

mysql> CREATE PROCEDURE dorepeat(p1 INT)
 -> BEGIN
 ->   SET @x = 0;
 ->   REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
 -> END
 -> //
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;

mysql> CALL dorepeat(1000);
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @x;
+------+
| @x   |
+------+
| 1001 |
+------+
1 row in set (0.00 sec)

您可以将分隔符重新定义为除//之外的其他字符串,并且分隔符可以由单个字符或多个字符组成。应避免使用反斜杠(\)字符,因为这是 MySQL 的转义字符。

以下是一个接受参数、使用 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)

27.2 使用存储过程

原文:dev.mysql.com/doc/refman/8.0/en/stored-routines.html

27.2.1 存储过程语法

27.2.2 存储过程和 MySQL 权限

27.2.3 存储过程元数据

27.2.4 存储过程、函数、触发器和 LAST_INSERT_ID()

MySQL 支持存储过程(过程和函数)。存储过程是一组可以存储在服务器中的 SQL 语句。一旦完成这个过程,客户端就不需要不断重新发出各个语句,而是可以引用存储过程。

在某些情况下,存储过程可能特别有用:

  • 当多个客户端应用程序使用不同语言编写或在不同平台上运行,但需要执行相同的数据库操作时。

  • 当安全性至关重要时。例如,银行使用存储过程和函数进行所有常见操作。这提供了一个一致且安全的环境,而例程可以确保每个操作都被正确记录。在这样的设置中,应用程序和用户无法直接访问数据库表,而只能执行特定的存储过程。

存储过程可以提供更好的性能,因为服务器和客户端之间需要传输的信息更少。这样做的代价是这会增加数据库服务器的负载,因为更多的工作是在服务器端完成的,而在客户端(应用程序)端完成的工作较少。如果许多客户机(如 Web 服务器)由一个或几个数据库服务器提供服务,请考虑这一点。

存储过程还使您能够在数据库服务器中拥有函数库。这是现代应用程序语言共享的功能,这些语言内部允许这样的设计(例如,通过使用类)。即使在数据库使用范围之外,利用这些客户端应用程序语言功能对程序员也是有益的。

MySQL 遵循 SQL:2003 的存储过程语法,这也是 IBM 的 DB2 所使用的。这里描述的所有语法都受支持,任何限制和扩展都在适当的地方有文档记录。

其他资源

  • 在处理存储过程和函数时,您可能会发现存储过程用户论坛很有用。

  • 有关 MySQL 中存储过程的一些常见问题的答案,请参阅附录 A.4,“MySQL 8.0 FAQ:存储过程和函数”。

  • 对于存储过程的使用有一些限制。请参阅第 27.8 节,“存储程序的限制”。

  • 存储过程的二进制日志记录过程如第 27.7 节,“存储程序二进制日志记录”中所述。

27.2.1 存储过程语法

原文:dev.mysql.com/doc/refman/8.0/en/stored-routines-syntax.html

存储过程可以是过程或函数。存储过程是使用CREATE PROCEDURECREATE FUNCTION语句创建的(参见 Section 15.1.17, “CREATE PROCEDURE and CREATE FUNCTION Statements”)。通过CALL语句调用过程(参见 Section 15.2.1, “CALL Statement”),只能使用输出变量传回值。函数可以像其他函数一样从语句内部调用(即通过调用函数的名称),并且可以返回一个标量值。存储过程的主体可以使用复合语句(参见 Section 15.6, “Compound Statement Syntax”)。

存储过程可以使用DROP PROCEDUREDROP FUNCTION语句删除(参见 Section 15.1.29, “DROP PROCEDURE and DROP FUNCTION Statements”),并且可以使用ALTER PROCEDUREALTER FUNCTION语句进行修改(参见 Section 15.1.7, “ALTER PROCEDURE Statement”)。

存储过程或函数与特定数据库相关联。这有几个含义:

  • 当调用例程时,会执行隐式的USE *db_name*(在例程终止时撤消)。存储过程内部不允许使用USE语句。

  • 您可以使用数据库名称限定例程名称。这可用于引用不在当前数据库中的例程。例如,要调用与test数据库关联的存储过程p或函数f,可以使用CALL test.p()test.f()

  • 当一个数据库被删除时,与之相关的所有存储过程也会被删除。

存储函数不能是递归的。

存储过程中允许递归,但默认情况下被禁用。要启用递归,请将max_sp_recursion_depth服务器系统变量设置为大于零的值。存储过程递归会增加线程堆栈空间的需求。如果增加max_sp_recursion_depth的值,可能需要通过增加服务器启动时thread_stack的值来增加线程堆栈大小。有关更多信息,请参见第 7.1.8 节,“服务器系统变量”。

MySQL 支持一个非常有用的扩展,允许在存储过程中使用常规的SELECT语句(即,不使用游标或本地变量)。这种查询的结果集直接发送到客户端。多个SELECT语句会生成多个结果集,因此客户端必须使用支持多个结果集的 MySQL 客户端库。这意味着客户端必须使用至少与 MySQL 4.1 版本一样新的客户端库。客户端在连接时还应指定CLIENT_MULTI_RESULTS选项。对于 C 程序,可以使用mysql_real_connect() C API 函数来实现。请参见 mysql_real_connect(),以及多语句执行支持。

在 MySQL 8.0.22 及更高版本中,存储过程中的语句引用的用户变量在存储过程首次调用时确定其类型,并在此后的每次调用存储过程时保留此类型。

27.2.2 存储例程和 MySQL 权限

原文:dev.mysql.com/doc/refman/8.0/en/stored-routines-privileges.html

MySQL 授权系统如下考虑存储例程:

  • 需要CREATE ROUTINE 权限来创建存储例程。

  • 需要ALTER ROUTINE 权限来修改或删除存储例程。如果需要,此权限将自动授予例程的创建者,并在删除例程时从创建者那里删除。

  • 执行存储例程需要EXECUTE 权限。但是,如果需要,此权限将自动授予例程的创建者(在删除例程时从创建者那里删除)。此外,例程的默认SQL SECURITY特性为DEFINER,这使得具有与例程关联的数据库访问权限的用户可以执行该例程。

  • 如果automatic_sp_privileges 系统变量为 0,则不会自动授予和删除例程创建者的EXECUTEALTER ROUTINE 权限。

  • 例程的创建者是用于执行其CREATE语句的帐户。这可能与例程定义中命名为DEFINER的帐户不同。

  • 例程DEFINER命名的帐户可以查看所有例程属性,包括其定义。因此,该帐户完全可以访问由以下产生的例程输出:

    • 信息模式ROUTINES 表的内容。

    • SHOW CREATE FUNCTIONSHOW CREATE PROCEDURE 语句。

    • SHOW FUNCTION CODESHOW PROCEDURE CODE 语句。

    • SHOW FUNCTION STATUSSHOW PROCEDURE STATUS 语句。

  • 对于非例程DEFINER命名的帐户,访问例程属性取决于授予帐户的权限:

    • 使用SHOW_ROUTINE 权限或全局SELECT 权限,帐户可以查看所有例程属性,包括其定义。

    • 通过在包含例程的范围内授予CREATE ROUTINEALTER ROUTINEEXECUTE权限,帐户可以查看所有例程属性,除了其定义。

27.2.3 存储过程元数据

原文:dev.mysql.com/doc/refman/8.0/en/stored-routines-metadata.html

获取存储过程的元数据:

  • 查询INFORMATION_SCHEMA数据库的ROUTINES表。参见 Section 28.3.30, “The INFORMATION_SCHEMA ROUTINES Table”。

  • 使用SHOW CREATE PROCEDURESHOW CREATE FUNCTION语句查看过程定义。参见 Section 15.7.7.9, “SHOW CREATE PROCEDURE Statement”。

  • 使用SHOW PROCEDURE STATUSSHOW FUNCTION STATUS语句查看过程特征。参见 Section 15.7.7.28, “SHOW PROCEDURE STATUS Statement”。

  • 使用SHOW PROCEDURE CODESHOW FUNCTION CODE语句查看过程的内部实现表示。参见 Section 15.7.7.27, “SHOW PROCEDURE CODE Statement”。

27.2.4 存储过程、函数、触发器和 LAST_INSERT_ID()

原文:dev.mysql.com/doc/refman/8.0/en/stored-routines-last-insert-id.html

在存储过程(procedure 或 function)或触发器的主体内,LAST_INSERT_ID()的值会像在这些对象的主体外执行语句时一样发生变化(参见第 14.15 节,“信息函数”)。存储过程或触发器对LAST_INSERT_ID()值的影响取决于存储过程的类型:

  • 如果存储过程执行更改LAST_INSERT_ID()值的语句,则后续语句会看到更改后的值。

  • 对于改变值的存储函数和触发器,在函数或触发器结束时,值会恢复,因此后续语句不会看到更改后的值。

27.3 使用触发器

原文:dev.mysql.com/doc/refman/8.0/en/triggers.html

27.3.1 触发器语法和示例

27.3.2 触发器元数据

触发器是与表关联的命名数据库对象,当表发生特定事件时激活。触发器的一些用途包括对要插入表中的值执行检查,或对参与更新的值执行计算。

当语句在关联表中插入、更新或删除行时,触发器被定义为激活。这些行操作是触发器事件。例如,行可以通过 INSERTLOAD DATA 语句插入,每插入一行触发器就会激活一次。触发器可以设置为在触发事件之前或之后激活。例如,您可以在将每行插入到表中之前或之后激活触发器。

重要提示

MySQL 触发器仅在通过 SQL 语句对表进行更改时激活。这包括对底层可更新视图的基本表进行的更改。触发器不会在通过不向 MySQL 服务器传输 SQL 语句的 API 对表进行的更改时激活。这意味着触发器不会被使用 NDB API 进行的更新所激活。

触发器不会因 INFORMATION_SCHEMAperformance_schema 表的更改而激活。这些表实际上是视图,视图上不允许触发器。

以下各节描述了创建和删除触发器的语法,展示了如何使用它们的一些示例,并指示如何获取触发器元数据。

其他资源

  • 在处理触发器时,您可能会发现 MySQL 用户论坛 有所帮助。

  • 有关 MySQL 中触发器常见问题的答案,请参阅 第 A.5 节 “MySQL 8.0 FAQ: 触发器”。

  • 对触发器的使用有一些限制;请参阅 第 27.8 节 “存储程序限制”。

  • 触发器的二进制日志记录如 第 27.7 节 “存储程序二进制日志记录” 中所述。

27.3.1 触发器语法和示例

原文:dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html

要创建触发器或删除触发器,请使用CREATE TRIGGERDROP TRIGGER语句,描述在第 15.1.22 节,“CREATE TRIGGER Statement”和第 15.1.34 节,“DROP TRIGGER Statement”中。

这里有一个简单的示例,将触发器与表关联起来,以激活INSERT操作。 触发器充当累加器,对表的某一列插入的值进行求和。

mysql> CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
Query OK, 0 rows affected (0.03 sec)

mysql> CREATE TRIGGER ins_sum BEFORE INSERT ON account
       FOR EACH ROW SET @sum = @sum + NEW.amount;
Query OK, 0 rows affected (0.01 sec)

CREATE TRIGGER语句创建一个名为ins_sum的触发器,与account表关联。 它还包括指定触发器动作时间、触发事件以及触发器激活时要执行的操作的子句:

  • 关键字BEFORE表示触发器动作时间。 在这种情况下,触发器在每行插入到表之前激活。 这里允许的其他关键字是AFTER

  • 关键字INSERT表示触发事件;也就是说,激活触发器的操作类型。 在示例中,INSERT操作会导致触发器激活。 您还可以为DELETEUPDATE操作创建触发器。

  • FOR EACH ROW后面的语句定义了触发器体;也就是说,每次触发器激活时执行的语句,这发生在触发事件影响的每一行。 在示例中,触发器体是一个简单的SET,将插入到amount列中的值累积到用户变量中。 该语句将列称为NEW.amount,意思是“要插入新行的amount列的值”。

要使用触发器,请将累加器变量设置为零,执行一个INSERT语句,然后查看变量之后的值:

mysql> SET @sum = 0;
mysql> INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);
mysql> SELECT @sum AS 'Total amount inserted';
+-----------------------+
| Total amount inserted |
+-----------------------+
|               1852.48 |
+-----------------------+

在这种情况下,INSERT语句执行后@sum的值为14.98 + 1937.50 - 100,即1852.48

要销毁触发器,请使用DROP TRIGGER语句。 如果触发器不在默认模式中,则必须指定模式名称:

mysql> DROP TRIGGER test.ins_sum;

如果删除表,则表的任何触发器也将被删除。

触发器名称存在于模式命名空间中,这意味着所有触发器在模式内必须具有唯一名称。 不同模式中的触发器可以具有相同的名称。

可以为给定表定义多个具有相同触发事件和操作时间的触发器。例如,您可以为表定义两个BEFORE UPDATE触发器。默认情况下,具有相同触发事件和操作时间的触发器按照它们创建的顺序激活。要影响触发器顺序,请在FOR EACH ROW之后指定一个子句,指示FOLLOWSPRECEDES以及一个具有相同触发事件和操作时间的现有触发器的名称。使用FOLLOWS,新触发器在现有触发器之后激活。使用PRECEDES,新触发器在现有触发器之前激活。

例如,以下触发器定义为account表定义了另一个BEFORE INSERT触发器:

mysql> CREATE TRIGGER ins_transaction BEFORE INSERT ON account
       FOR EACH ROW PRECEDES ins_sum
       SET
       @deposits = @deposits + IF(NEW.amount>0,NEW.amount,0),
       @withdrawals = @withdrawals + IF(NEW.amount<0,-NEW.amount,0);
Query OK, 0 rows affected (0.01 sec)

此触发器ins_transaction类似于ins_sum,但分别累积存款和取款。它具有一个PRECEDES子句,使其在ins_sum之前激活;如果没有该子句,它将在ins_sum之后激活,因为它是在ins_sum之后创建的。

在触发器主体内,OLDNEW关键字使您能够访问触发器影响的行中的列。OLDNEW是 MySQL 触发器的扩展;它们不区分大小写。

INSERT触发器中,只能使用NEW.*col_name*;没有旧行。在DELETE触发器中,只能使用OLD.*col_name*;没有新行。在UPDATE触发器中,您可以使用OLD.*col_name*来引用更新前行的列,使用NEW.*col_name*来引用更新后行的列。

OLD命名的列是只读的。您可以引用它(如果您具有SELECT权限),但不能修改它。如果您对其具有SELECT权限,则可以引用以NEW命名的列。在BEFORE触发器中,如果您对其具有UPDATE权限,则还可以使用SET NEW.*col_name* = *value*来更改其值。这意味着您可以使用触发器修改要插入新行或用于更新行的值。(在AFTER触发器中,这样的SET语句没有效果,因为行更改已经发生。)

BEFORE触发器中,AUTO_INCREMENT列的NEW值为 0,而不是在实际插入新行时自动生成的序列号。

通过使用BEGIN ... END结构,可以定义执行多个语句的触发器。在BEGIN块内,还可以使用其他在存储过程中允许的语法,如条件和循环。然而,就像对于存储过程一样,如果使用mysql程序定义执行多个语句的触发器,则需要重新定义mysql语句定界符,以便在触发器定义内使用;语句定界符。以下示例说明了这些要点。它定义了一个UPDATE触发器,检查要用于更新每行的新值,并修改值使其在 0 到 100 的范围内。这必须是BEFORE触发器,因为必须在使用该值更新行之前检查该值:

mysql> delimiter //
mysql> CREATE TRIGGER upd_check BEFORE UPDATE ON account
       FOR EACH ROW
       BEGIN
           IF NEW.amount < 0 THEN
               SET NEW.amount = 0;
           ELSEIF NEW.amount > 100 THEN
               SET NEW.amount = 100;
           END IF;
       END;//
mysql> delimiter ;

将存储过程单独定义,然后使用简单的CALL语句从触发器中调用它可能更容易。如果要从多个触发器内执行相同的代码,这也是有利的。

触发器执行时,触发器执行的语句中出现的内容有限制:

  • 触发器不能使用CALL语句调用返回数据给客户端或使用动态 SQL 的存储过程。(存储过程可以通过OUTINOUT参数将数据返回给触发器。)

  • 触发器不能使用明确或隐式开始或结束事务的语句,例如START TRANSACTIONCOMMITROLLBACK。(ROLLBACK to SAVEPOINT是允许的,因为它不会结束事务。)。

另请参阅 Section 27.8, “存储程序的限制”。

MySQL 在触发器执行期间处理错误如下:

  • 如果BEFORE触发器失败,则不会执行对应行的操作。

  • BEFORE触发器在尝试插入或修改行时被激活,无论尝试是否成功。

  • 仅当任何BEFORE触发器和行操作成功执行时,才会执行AFTER触发器。

  • BEFOREAFTER触发器中的错误导致触发器调用的整个语句失败。

  • 对于事务性表,语句失败应导致语句执行的所有更改回滚。触发器失败会导致语句失败,因此触发器失败也会导致回滚。对于非事务性表,无法进行此类回滚,因此尽管语句失败,但在错误点之前执行的任何更改仍然有效。

触发器可以通过名称直接引用表,例如此示例中显示的名为testref的触发器:

CREATE TABLE test1(a1 INT);
CREATE TABLE test2(a2 INT);
CREATE TABLE test3(a3 INT NOT NULL AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE test4(
  a4 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  b4 INT DEFAULT 0
);

delimiter |

CREATE TRIGGER testref BEFORE INSERT ON test1
  FOR EACH ROW
  BEGIN
    INSERT INTO test2 SET a2 = NEW.a1;
    DELETE FROM test3 WHERE a3 = NEW.a1;
    UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;
  END;
|

delimiter ;

INSERT INTO test3 (a3) VALUES
  (NULL), (NULL), (NULL), (NULL), (NULL),
  (NULL), (NULL), (NULL), (NULL), (NULL);

INSERT INTO test4 (a4) VALUES
  (0), (0), (0), (0), (0), (0), (0), (0), (0), (0);

假设您将以下数值插入到表test1中,如下所示:

mysql> INSERT INTO test1 VALUES 
       (1), (3), (1), (7), (1), (8), (4), (4);
Query OK, 8 rows affected (0.01 sec)
Records: 8  Duplicates: 0  Warnings: 0

结果,四个表包含以下数据:

mysql> SELECT * FROM test1;
+------+
| a1   |
+------+
|    1 |
|    3 |
|    1 |
|    7 |
|    1 |
|    8 |
|    4 |
|    4 |
+------+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test2;
+------+
| a2   |
+------+
|    1 |
|    3 |
|    1 |
|    7 |
|    1 |
|    8 |
|    4 |
|    4 |
+------+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test3;
+----+
| a3 |
+----+
|  2 |
|  5 |
|  6 |
|  9 |
| 10 |
+----+
5 rows in set (0.00 sec)

mysql> SELECT * FROM test4;
+----+------+
| a4 | b4   |
+----+------+
|  1 |    3 |
|  2 |    0 |
|  3 |    1 |
|  4 |    2 |
|  5 |    0 |
|  6 |    0 |
|  7 |    1 |
|  8 |    1 |
|  9 |    0 |
| 10 |    0 |
+----+------+
10 rows in set (0.00 sec)

27.3.2 触发器元数据

原文:dev.mysql.com/doc/refman/8.0/en/trigger-metadata.html

要获取有关触发器的元数据:

  • 查询INFORMATION_SCHEMA数据库的TRIGGERS表。参见 Section 28.3.45, “The INFORMATION_SCHEMA TRIGGERS Table”。

  • 使用SHOW CREATE TRIGGER语句。参见 Section 15.7.7.11, “SHOW CREATE TRIGGER Statement”。

  • 使用SHOW TRIGGERS语句。参见 Section 15.7.7.40, “SHOW TRIGGERS Statement”。

27.4 使用事件调度器

原文:dev.mysql.com/doc/refman/8.0/en/event-scheduler.html

27.4.1 事件调度器概述

27.4.2 事件调度器配置

27.4.3 事件语法

27.4.4 事件元数据

27.4.5 事件调度器状态

27.4.6 事件调度器和 MySQL 权限

MySQL 事件调度器管理事件的调度和执行,即按照时间表运行的任务。以下讨论涵盖了事件调度器,并分为以下几个部分:

  • 第 27.4.1 节,“事件调度器概述”,介绍了 MySQL 事件的简介和概念概述。

  • 第 27.4.3 节,“事件语法”,讨论了用于创建、修改和删除 MySQL 事件的 SQL 语句。

  • 第 27.4.4 节,“事件元数据”,展示了如何获取有关事件的信息以及 MySQL 服务器如何存储这些信息。

  • 第 27.4.6 节,“事件调度器和 MySQL 权限”,讨论了处理事件所需的权限以及在执行时事件对权限的影响。

存储过程需要mysql系统数据库中的events数据字典表。此表在 MySQL 8.0 安装过程中创建。如果您从早期版本升级到 MySQL 8.0,请确保执行升级过程,以确保您的系统数据库是最新的。请参阅第三章,升级 MySQL

其他资源

  • 对事件的使用有一些限制;请参阅第 27.8 节,“存储程序的限制”。

  • 事件的二进制日志记录如第 27.7 节,“存储程序的二进制日志记录”所述进行。

  • 您可能还会发现MySQL 用户论坛很有帮助。

27.4.1 事件调度程序概述

原文:dev.mysql.com/doc/refman/8.0/en/events-overview.html

MySQL 事件是按照时间表运行的任务。因此,我们有时将它们称为定时事件。当您创建一个事件时,实际上是创建了一个命名的数据库对象,其中包含一个或多个要在一个或多个常规间隔执行的 SQL 语句,开始和结束于特定日期和时间。从概念上讲,这类似于 Unix 的crontab(也称为“cron job”)或 Windows 任务计划程序的概念。

这种类型的定时任务有时也被称为“时间触发器”,暗示这些是由时间流逝触发的对象。虽然这基本上是正确的,但我们更倾向于使用术语事件,以避免与第 27.3 节“使用触发器”中讨论的触发器混淆。事件更具体地不应与“临时触发器”混淆。触发器是一个数据库对象,其语句在发生在给定表上的特定类型事件时执行,而(定时)事件是一个对象,其语句在经过指定的时间间隔后执行。

虽然 SQL 标准中没有关于事件调度的规定,但其他数据库系统中有先例,您可能会注意到这些实现与 MySQL 服务器中的实现之间的一些相似之处。

MySQL 事件具有以下主要特性和属性:

  • 在 MySQL 中,事件通过其名称和分配给它的模式唯一标识。

  • 事件根据时间表执行特定操作。此操作包括一个 SQL 语句,如果需要,可以是一个BEGIN ... END块中的复合语句(请参阅第 15.6 节“复合语句语法”)。事件的时间可以是一次性的或循环的。一次性事件仅执行一次。循环事件在常规间隔重复其操作,并且循环事件的时间表可以分配一个特定的开始日期和时间,结束日期和时间,两者都有,或两者都没有。(默认情况下,循环事件的时间表在创建时立即开始,并持续无限期,直到被禁用或删除。)

    如果重复事件在其调度间隔内没有终止,则可能会导致多个事件实例同时执行。如果这是不希望的,您应该实施一种机制来防止同时实例。例如,您可以使用GET_LOCK()函数,或行或表锁定。

  • 用户可以使用专为此目的设计的 SQL 语句创建、修改和删除计划事件。语法无效的事件创建和修改语句将失败,并显示适当的错误消息。用户可能在事件的操作中包含需要用户实际上没有的权限的语句。事件创建或修改语句成功,但事件的操作失败。有关详细信息,请参见 第 27.4.6 节,“事件调度程序和 MySQL 权限”。

  • 许多事件的属性可以使用 SQL 语句进行设置或修改。这些属性包括事件的名称、定时、持久性(即在其计划过期后是否保留)、状态(启用或禁用)、要执行的操作以及分配给它的模式。参见 第 15.1.3 节,“ALTER EVENT 语句”。

    事件的默认定义者是创建事件的用户,除非事件已被修改,在这种情况下,定义者是发出影响该事件的最后一个 ALTER EVENT 语句的用户。任何具有对定义事件的数据库上的 EVENT 权限的用户都可以修改事件。有关详细信息,请参见 第 27.4.6 节,“事件调度程序和 MySQL 权限”。

  • 事件的操作语句可以包含大多数存储过程中允许的 SQL 语句。有关限制,请参见 第 27.8 节,“存储程序的限制”。

27.4.2 事件调度器配置

原文:dev.mysql.com/doc/refman/8.0/en/events-configuration.html

事件由一个特殊的事件调度器线程执行;当我们提到事件调度器时,实际上是指这个线程。当运行时,具有PROCESS权限的用户可以在SHOW PROCESSLIST的输出中看到事件调度器线程及其当前状态,如下面的讨论所示。

全局event_scheduler系统变量确定事件调度器在服务器上是否启用和运行。它具有以下值之一,这些值会影响事件调度的描述:

  • ON: 事件调度器已启动;事件调度器线程运行并执行所有预定事件。ON是默认的event_scheduler值。

    当事件调度器为ON时,事件调度器线程将作为守护进程在SHOW PROCESSLIST的输出中列出,并且其状态如下所示:

    mysql> SHOW PROCESSLIST\G
    *************************** 1\. row ***************************
         Id: 1
       User: root
       Host: localhost
         db: NULL
    Command: Query
       Time: 0
      State: NULL
       Info: show processlist
    *************************** 2\. row ***************************
         Id: 2
       User: event_scheduler
       Host: localhost
         db: NULL
    Command: Daemon
       Time: 3
      State: Waiting for next activation
       Info: NULL 2 rows in set (0.00 sec)
    

    通过将event_scheduler的值设置为OFF来停止事件调度。

  • OFF: 事件调度器已停止。事件调度器线程不运行,不会显示在SHOW PROCESSLIST的输出中,也不会执行任何预定事件。

    当事件调度器停止时(event_schedulerOFF),可以通过将event_scheduler的值设置为ON来启动它。(见下一项。)

  • DISABLED: 此值使事件调度器无法运行。当事件调度器为DISABLED时,事件调度器线程不运行(因此不会出现在SHOW PROCESSLIST的输出中)。此外,事件调度器状���无法在运行时更改。

如果事件调度器状态未设置为DISABLED,则可以在event_scheduler的值之间切换为ONOFF(使用SET)。在设置此变量时,也可以使用0表示OFF,使用1表示ON。因此,在mysql客户端中可以使用以下任何一条语句来启动事件调度器:

SET GLOBAL event_scheduler = ON;
SET @@GLOBAL.event_scheduler = ON;
SET GLOBAL event_scheduler = 1;
SET @@GLOBAL.event_scheduler = 1;

类似地,可以使用以下任何一条语句关闭事件调度器:

SET GLOBAL event_scheduler = OFF;
SET @@GLOBAL.event_scheduler = OFF;
SET GLOBAL event_scheduler = 0;
SET @@GLOBAL.event_scheduler = 0;

注意

如果事件调度器已启用,则启用super_read_only系统变量会阻止其在events数据字典表中更新事件“上次执行”时间戳。这会导致事件调度器在下次尝试执行计划事件时停止,并在写入服务器错误日志后发出消息。(在这种情况下,event_scheduler系统变量不会从ON更改为OFF。一个含义是,此变量拒绝了 DBA 意图,即事件调度器启用或禁用,其实际状态为启动或停止可能是不同的。)如果在启用后随后禁用super_read_only,服务器会根据需要自动重新启动事件调度器,截至 MySQL 8.0.26。在 MySQL 8.0.26 之前,需要手动重新启动事件调度器以再次启用它。

尽管ONOFF有数值等价物,但由SELECTSHOW VARIABLES显示的event_scheduler的值始终为OFFONDISABLEDDISABLED没有数值等价物。因此,在设置此变量时,通常优先选择ONOFF而不是10

请注意,尝试设置event_scheduler而不将其指定为全局变量会导致错误:

mysql< SET @@event_scheduler = OFF;
ERROR 1229 (HY000): Variable 'event_scheduler' is a GLOBAL
variable and should be set with SET GLOBAL

重要

只能在服务器启动时将事件调度器设置为DISABLED。如果event_schedulerONOFF,则无法在运行时将其设置为DISABLED。此外,如果事件调度器在启动时设置为DISABLED,则无法在运行时更改event_scheduler的值。

要禁用事件调度器,请使用以下两种方法之一:

  • 作为启动服务器时的命令行选项:

    --event-scheduler=DISABLED
    
  • 在服务器配置文件(my.cnf,或 Windows 系统上的my.ini)中,包含可以被服务器读取的行(例如,在[mysqld]部分):

    event_scheduler=DISABLED
    

要启用事件调度器,请在重新启动服务器时不使用--event-scheduler=DISABLED命令行选项,或在服务器配置文件中删除或注释包含event-scheduler=DISABLED的行,或者在适当的情况下。另外,当启动服务器时,您可以使用ON(或1)或OFF(或0)来代替DISABLED值。

注意

event_scheduler设置为DISABLED时,可以发出事件操作语句。在这种情况下不会生成警告或错误(前提是语句本身有效)。但是,在将此变量设置为ON(或1)之前,计划事件无法执行。一旦完成此操作,事件调度程序线程将执行所有满足调度条件的事件。

使用--skip-grant-tables选项启动 MySQL 服务器会将event_scheduler设置为DISABLED,覆盖在命令行或my.cnfmy.ini文件中设置的任何其他值(Bug #26807)。

有关用于创建、修改和删除事件的 SQL 语句,请参见第 27.4.3 节,“事件语法”。

MySQL 在INFORMATION_SCHEMA数据库中提供了一个EVENTS表。可以查询此表以获取关于服务器上已定义的计划事件的信息。更多信息请参见第 27.4.4 节,“事件元数据”和第 28.3.14 节,“INFORMATION_SCHEMA EVENTS 表”。

有关事件调度和 MySQL 权限系统的信息,请参见第 27.4.6 节,“事件调度程序和 MySQL 权限”。