文档首页
MySQL 9.0 参考手册
相关文档 下载本手册
PDF(美式信纸) - 40.0Mb
PDF (A4) - 40.1Mb
手册页 (TGZ) - 258.2Kb
手册页 (Zip) - 365.3Kb
信息 (Gzip) - 4.0Mb
信息 (Zip) - 4.0Mb


MySQL 9.0 参考手册  /  ...  /  CREATE EVENT 语句

15.1.13 CREATE EVENT 语句

CREATE
    [DEFINER = user]
    EVENT
    [IF NOT EXISTS]
    event_name
    ON SCHEDULE schedule
    [ON COMPLETION [NOT] PRESERVE]
    [ENABLE | DISABLE | DISABLE ON {REPLICA | SLAVE}]
    [COMMENT 'string']
    DO event_body;

schedule: {
    AT timestamp [+ INTERVAL interval] ...
  | EVERY interval
    [STARTS timestamp [+ INTERVAL interval] ...]
    [ENDS timestamp [+ INTERVAL interval] ...]
}

interval:
    quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |
              WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |
              DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}

此语句创建并调度一个新事件。除非启用了事件调度程序,否则该事件不会运行。有关检查事件调度程序状态并在必要时启用它的信息,请参见 第 27.5.2 节“事件调度程序配置”

CREATE EVENT 需要对要在其中创建事件的模式具有 EVENT 权限。如果存在 DEFINER 子句,则所需的权限取决于 user 值,如 第 27.7 节“存储对象访问控制” 中所述。

有效 CREATE EVENT 语句的最低要求如下:

  • 关键字 CREATE EVENT 加上事件名称,该名称在数据库模式中唯一标识该事件。

  • 一个 ON SCHEDULE 子句,用于确定事件的执行时间和频率。

  • 一个 DO 子句,其中包含要由事件执行的 SQL 语句。

这是一个最简单的 CREATE EVENT 语句的示例:

CREATE EVENT myevent
    ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
    DO
      UPDATE myschema.mytable SET mycol = mycol + 1;

上一条语句创建了一个名为 myevent 的事件。此事件将在创建后一小时执行一次,执行的 SQL 语句会将 myschema.mytable 表的 mycol 列的值增加 1。

event_name 必须是有效的 MySQL 标识符,最大长度为 64 个字符。事件名称不区分大小写,因此在同一架构中不能有两个名为 myeventMyEvent 的事件。通常,事件名称的规则与存储例程名称的规则相同。请参阅 第 11.2 节“架构对象名称”

事件与架构相关联。如果 event_name 中未指定架构,则假定为默认(当前)架构。要在特定架构中创建事件,请使用 schema_name.event_name 语法限定事件名称。

DEFINER 子句指定在事件执行时用于检查访问权限的 MySQL 帐户。如果存在 DEFINER 子句,则 user 值应为指定为 'user_name'@'host_name' 的 MySQL 帐户、CURRENT_USERCURRENT_USER()。允许的 user 值取决于您拥有的权限,如 第 27.7 节“存储对象访问控制” 中所述。另请参阅该部分以获取有关事件安全的更多信息。

如果省略 DEFINER 子句,则默认定义者是执行 CREATE EVENT 语句的用户。这与显式指定 DEFINER = CURRENT_USER 相同。

在事件体中,CURRENT_USER 函数返回用于在事件执行时检查权限的帐户,该帐户是 DEFINER 用户。有关事件内用户审核的信息,请参阅 第 8.2.23 节“基于 SQL 的帐户活动审核”

IF NOT EXISTS 对于 CREATE EVENT 的含义与 CREATE TABLE 相同:如果在同一架构中已存在名为 event_name 的事件,则不执行任何操作,也不会产生错误。(但是,在这种情况下会生成警告。)

ON SCHEDULE 子句确定为事件定义的 event_body 何时、多久重复一次以及重复多长时间。此子句采用以下两种形式之一

  • AT timestamp 用于一次性事件。它指定事件仅在 timestamp 给定的日期和时间执行一次,timestamp 必须同时包含日期和时间,或者必须是解析为日期时间值的表达式。为此,您可以使用 DATETIMETIMESTAMP 类型的值。如果日期已过,则会发出警告,如下所示

    mysql> SELECT NOW();
    +---------------------+
    | NOW()               |
    +---------------------+
    | 2006-02-10 23:59:01 |
    +---------------------+
    1 row in set (0.04 sec)
    
    mysql> CREATE EVENT e_totals
        ->     ON SCHEDULE AT '2006-02-10 23:59:00'
        ->     DO INSERT INTO test.totals VALUES (NOW());
    Query OK, 0 rows affected, 1 warning (0.00 sec)
    
    mysql> SHOW WARNINGS\G
    *************************** 1. row ***************************
      Level: Note
       Code: 1588
    Message: Event execution time is in the past and ON COMPLETION NOT
             PRESERVE is set. The event was dropped immediately after
             creation.

    本身无效的 CREATE EVENT 语句(无论出于何种原因)都会失败并报错。

    您可以使用 CURRENT_TIMESTAMP 指定当前日期和时间。在这种情况下,事件会在创建后立即生效。

    要创建一个在相对于当前日期和时间的未来某个时间点发生的事件(例如“三周后”所表达的事件),可以使用可选子句 + INTERVAL intervalinterval 部分由时间量和时间单位两部分组成,并遵循 时间间隔 中描述的语法规则,但定义事件时不能使用任何涉及微秒的单位关键字。对于某些时间间隔类型,可以使用复杂的时间单位。例如,“两分钟零十秒”可以表示为 + INTERVAL '2:10' MINUTE_SECOND

    您还可以组合时间间隔。例如,AT CURRENT_TIMESTAMP + INTERVAL 3 WEEK + INTERVAL 2 DAY 等价于“三周零两天后”。此类子句的每个部分都必须以 + INTERVAL 开头。

  • 要按固定间隔重复操作,请使用 EVERY 子句。EVERY 关键字后跟一个 interval,如前面讨论 AT 关键字时所述。(+ INTERVAL EVERY 一起使用。)例如,EVERY 6 WEEK 表示“每六周”。

    尽管 + INTERVAL 子句在 EVERY 子句中不允许使用,但您可以使用 + INTERVAL 中允许的相同复杂时间单位。

    EVERY 子句可以包含一个可选的 STARTS 子句。STARTS 后跟一个 timestamp 值,该值指示操作应何时开始重复,并且还可以使用 + INTERVAL interval 指定“从现在开始”的时间量。例如,EVERY 3 MONTH STARTS CURRENT_TIMESTAMP + INTERVAL 1 WEEK 表示“每三个月,从现在开始的一周后开始”。类似地,您可以将“每两周,从现在开始的六小时十五分钟后开始”表示为 EVERY 2 WEEK STARTS CURRENT_TIMESTAMP + INTERVAL '6:15' HOUR_MINUTE。不指定 STARTS 与使用 STARTS CURRENT_TIMESTAMP 相同,也就是说,为事件指定的操作在创建事件后立即开始重复。

    EVERY 子句可以包含一个可选的 ENDS 子句。ENDS 关键字后跟一个 timestamp 值,该值告诉 MySQL 事件应何时停止重复。您还可以将 + INTERVAL intervalENDS 一起使用;例如,EVERY 12 HOUR STARTS CURRENT_TIMESTAMP + INTERVAL 30 MINUTE ENDS CURRENT_TIMESTAMP + INTERVAL 4 WEEK 等价于“每十二个小时,从现在开始的三十分钟后开始,到从现在开始的四周后结束”。不使用 ENDS 表示事件将无限期地继续执行。

    ENDS 支持与 STARTS 相同的复杂时间单位语法。

    您可以在 EVERY 子句中使用 STARTSENDS、两者都使用或两者都不使用。

    如果重复事件在其调度间隔内未终止,则可能会导致事件的多个实例同时执行。如果这是不可取的,则应建立一种机制来防止同时出现多个实例。例如,您可以使用 GET_LOCK() 函数,或者使用行锁或表锁。

ON SCHEDULE 子句可以使用涉及内置 MySQL 函数和用户变量的表达式来获取其包含的任何 timestampinterval 值。您不能在此类表达式中使用存储函数或可加载函数,也不能使用任何表引用;但是,您可以使用 SELECT FROM DUAL。这对于 CREATE EVENTALTER EVENT 语句都是如此。在这种情况下,明确禁止引用存储函数、可加载函数和表,并且会失败并报错(请参阅错误 #22830)。

ON SCHEDULE 子句中的时间使用当前会话 time_zone 值进行解释。这将成为事件时区;也就是说,用于事件调度并在事件执行期间生效的时区。这些时间将转换为 UTC,并与事件时区一起存储在内部。这使得事件执行能够按照定义进行,而无需考虑服务器时区或夏令时效果的任何后续更改。有关事件时间表示的更多信息,请参阅 第 27.5.4 节“事件元数据”。另请参阅 第 15.7.7.19 节“SHOW EVENTS 语句”第 28.3.14 节“INFORMATION_SCHEMA EVENTS 表”

通常,事件过期后会立即删除。您可以通过指定 ON COMPLETION PRESERVE 来覆盖此行为。使用 ON COMPLETION NOT PRESERVE 只是使默认的非持久行为变得明确。

您可以创建一个事件,但可以使用 DISABLE 关键字阻止其处于活动状态。或者,您可以使用 ENABLE 明确默认状态,即活动状态。这与 ALTER EVENT 一起使用时最有用(请参阅 第 15.1.3 节“ALTER EVENT 语句”)。

第三个值也可以出现在 ENABLEDISABLE 的位置;DISABLE ON REPLICA 用于设置副本上事件的状态,以指示该事件是在复制源服务器上创建并复制到副本的,但在副本上不执行。请参阅 第 19.5.1.16 节“已调用功能的复制”

DISABLE ON REPLICA 替换了 DISABLE ON SLAVE,后者已被弃用,因此将在未来版本的 MySQL 中删除。

您可以使用 COMMENT 子句为事件提供注释。comment 可以是您希望用于描述事件的任何字符串,最长为 64 个字符。注释文本是字符串文字,因此必须用引号引起来。

DO 子句指定事件执行的操作,并由一条 SQL 语句组成。几乎所有可以在存储例程中使用的有效 MySQL 语句都可以用作计划事件的操作语句。(请参阅 第 27.9 节“对存储程序的限制”。)例如,以下事件 e_hourly 每小时删除一次 sessions 表中的所有行,其中该表是 site_activity 架构的一部分

CREATE EVENT e_hourly
    ON SCHEDULE
      EVERY 1 HOUR
    COMMENT 'Clears out sessions table each hour.'
    DO
      DELETE FROM site_activity.sessions;

MySQL 存储在创建或更改事件时生效的 sql_mode 系统变量设置,并始终使用此设置强制执行事件,而不管事件开始执行时的当前服务器 SQL 模式是什么

包含 DO 子句中嵌套 ALTER EVENT 语句的 CREATE EVENT 语句表面上可以成功执行;但是,当服务器尝试执行生成的计划事件时,执行会失败并报错。

注意

诸如 SELECTSHOW 之类的仅仅返回结果集的语句在事件中使用时无效;这些语句的输出不会发送到 MySQL 监视器,也不会存储在任何地方。但是,您可以使用诸如 SELECT ... INTOINSERT INTO ... SELECT 之类的存储结果的语句。(有关后者的实例,请参见本节中的下一个示例。)

事件所属的模式是 DO 子句中表引用的默认模式。对其他模式中表的任何引用都必须使用正确的模式名称进行限定。

与存储过程一样,您可以在 DO 子句中使用复合语句语法,方法是使用 BEGINEND 关键字,如下所示

delimiter |

CREATE EVENT e_daily
    ON SCHEDULE
      EVERY 1 DAY
    COMMENT 'Saves total number of sessions then clears the table each day'
    DO
      BEGIN
        INSERT INTO site_activity.totals (time, total)
          SELECT CURRENT_TIMESTAMP, COUNT(*)
            FROM site_activity.sessions;
        DELETE FROM site_activity.sessions;
      END |

delimiter ;

此示例使用 delimiter 命令更改语句分隔符。请参阅第 27.1 节“定义存储程序”

在事件中可以使用更复杂的复合语句,例如存储例程中使用的语句。此示例使用局部变量、错误处理程序和流程控制结构

delimiter |

CREATE EVENT e
    ON SCHEDULE
      EVERY 5 SECOND
    DO
      BEGIN
        DECLARE v INTEGER;
        DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;

        SET v = 0;

        WHILE v < 5 DO
          INSERT INTO t1 VALUES (0);
          UPDATE t2 SET s1 = s1 + 1;
          SET v = v + 1;
        END WHILE;
    END |

delimiter ;

没有办法直接向事件传递参数或从事件传递参数;但是,可以在事件中使用参数调用存储过程

CREATE EVENT e_call_myproc
    ON SCHEDULE
      AT CURRENT_TIMESTAMP + INTERVAL 1 DAY
    DO CALL myproc(5, 27);

在 MySQL 9.0 中,可以准备 CREATE EVENT 语句,但语句文本不能包含任何占位符 (?)。解决此限制的一种方法是组合语句的文本,准备它,并在存储过程中执行它;CREATE EVENT 语句的可变部分可以作为参数传递给存储过程。我们将在以下示例中对此进行演示,该示例假设数据库 d 中已经存在一个表 t,如下所示

USE d;

CREATE TABLE t (
  c1 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  c2 VARCHAR(20),
  c3 INT
);

我们希望创建一个事件,以在创建时确定的时间间隔将行插入此表中,类似于由以下语句定义的事件

CREATE EVENT e 
  ON SCHEDULE EVERY interval SECOND
  STARTS CURRENT_TIMESTAMP + INTERVAL 10 SECOND
  ENDS CURRENT_TIMESTAMP + INTERVAL 2 MINUTE
  ON COMPLETION PRESERVE
  DO
    INSERT INTO d.t1 VALUES ROW(NULL, NOW(), FLOOR(RAND()*100));

我们不能使用 ? 作为 interval 的占位符,但我们可以将参数值传递给存储过程,如下所示

delimiter |

CREATE PROCEDURE sp(n INT)
BEGIN
  SET @s1 = "CREATE EVENT e ON SCHEDULE EVERY ";
  SET @s2 = " SECOND
       STARTS CURRENT_TIMESTAMP + INTERVAL 10 SECOND
       ENDS CURRENT_TIMESTAMP + INTERVAL 2 MINUTE
       ON COMPLETION PRESERVE
       DO
       INSERT INTO d.t VALUES ROW(NULL, NOW(), FLOOR(RAND()*100))";
  
  SET @s = CONCAT(@s1, n, @s2);
  PREPARE ps FROM @s;
  EXECUTE ps;
  DEALLOCATE PREPARE ps;
END |

delimiter ;
mysql> TABLE t;
Empty set (0.00 sec)

mysql> CALL sp(5);
Query OK, 0 rows affected (0.01 sec)

# Wait 2 minutes...

mysql> TABLE t;
+----+---------------------+------+
| c1 | c2                  | c3   |
+----+---------------------+------+
|  1 | 2024-06-12 15:53:36 |   41 |
|  2 | 2024-06-12 15:53:41 |   84 |
|  3 | 2024-06-12 15:53:46 |   71 |
|  4 | 2024-06-12 15:53:51 |   78 |
|  5 | 2024-06-12 15:53:56 |   53 |
|  6 | 2024-06-12 15:54:01 |    6 |
|  7 | 2024-06-12 15:54:06 |   48 |
|  8 | 2024-06-12 15:54:11 |   98 |
|  9 | 2024-06-12 15:54:16 |   22 |
| 10 | 2024-06-12 15:54:21 |   88 |
| 11 | 2024-06-12 15:54:26 |   53 |
| 12 | 2024-06-12 15:54:31 |   75 |
| 13 | 2024-06-12 15:54:36 |   93 |
| 14 | 2024-06-12 15:54:41 |   13 |
| 15 | 2024-06-12 15:54:46 |   62 |
| 16 | 2024-06-12 15:54:51 |   47 |
| 17 | 2024-06-12 15:54:56 |   22 |
| 18 | 2024-06-12 15:55:01 |   47 |
| 19 | 2024-06-12 15:55:06 |   43 |
| 20 | 2024-06-12 15:55:11 |   50 |
| 21 | 2024-06-12 15:55:16 |   98 |
| 22 | 2024-06-12 15:55:21 |   15 |
| 23 | 2024-06-12 15:55:26 |   56 |
+----+---------------------+------+
23 rows in set (0.00 sec)

使用参数值 5 调用 sp(如下所示)并在事件 e 完成运行之前等待 2 分钟后,我们可以看到表 t 每 5 秒更新一次。由于 e 是使用 ON COMPLETION PRESERVE 创建的,因此我们可以在信息架构 EVENTS 表中看到它,并验证它是否按预期创建

mysql> SELECT EVENT_NAME, EVENT_SCHEMA, EVENT_DEFINITION, EVENT_TYPE 
     > FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME='e'\G
*************************** 1. row ***************************
      EVENT_NAME: e
    EVENT_SCHEMA: d
EVENT_DEFINITION: INSERT INTO d.t VALUES ROW(NULL, NOW(), FLOOR(RAND()*100))
      EVENT_TYPE: RECURRING
1 row in set (0.00 sec)

如果事件的定义者具有足够的权限来设置全局系统变量(请参阅第 7.1.9.1 节“系统变量权限”),则该事件可以读取和写入全局变量。由于授予此类权限可能会导致滥用,因此必须格外小心。

通常,存储例程中有效的任何语句都可以用于事件执行的操作语句。有关存储例程中允许使用的语句的更多信息,请参阅第 27.2.1 节“存储例程语法”。无法在存储例程中创建事件,也无法通过另一个事件创建事件。