MySQL 8.4 Reference Manual  /  Stored Objects  /  Restrictions on Stored Programs

27.8 存储程序的限制

这些限制适用于第 27 章 存储对象中描述的特性。

一些限制适用于所有存储程序,即存储过程和存储函数。还有一些限制专门适用于存储函数,而不是存储过程。

触发器的限制也适用于触发器。还有一些限制专门适用于触发器

存储过程的限制也适用于DO事件调度事件定义的DO子句。还有一些限制专门适用于事件

SQL 语句不允许在存储程序中使用

存储程序不能包含任意的 SQL 语句。以下语句不允许:

  • 锁定语句LOCK TABLESUNLOCK TABLES

  • ALTER VIEW.

  • LOAD DATALOAD XML.

  • SQL 预编译语句(PREPAREEXECUTEDEALLOCATE PREPARE)可以在存储程序中使用,但不能在存储函数或触发器中使用。因此,存储函数和触发器不能使用动态 SQL(您可以构建语句作为字符串,然后执行它们)。

  • 通常,不能在 SQL 预编译语句中使用的语句也不能在存储程序中使用。有关支持的预编译语句列表,请见第15.5节,“预编译语句”。例外是SIGNALRESIGNALGET DIAGNOSTICS,这些语句不能作为预编译语句使用,但可以在存储程序中使用。

  • 由于局部变量仅在存储程序执行期间有效,因此对它们的引用在预编译语句中不可用。预编译语句的作用域是当前会话,而不是存储程序,因此语句可以在程序结束后执行,这时变量将不再有效。例如,SELECT ... INTO local_var不能作为预编译语句使用。这一限制也适用于存储程序和函数参数。请见第15.5.1节,“PREPARE 语句”

  • 在所有存储程序(存储程序、函数、触发器和事件)中,解析器将BEGIN [WORK]视为BEGIN ... END 块的开始。要在这种上下文中开始事务,请使用START TRANSACTION代替。

以下语句或操作在存储函数中不可用。它们在存储程序中可用,但不能在存储函数或触发器中调用。例如,如果在存储程序中使用FLUSH,那么该存储程序不能在存储函数或触发器中调用。

  • 执行明确或隐式的事务提交或回滚。SQL 标准不要求每个 DBMS 提供这些语句的支持,SQL 标准指出每个 DBMS 可以决定是否允许它们。

  • 返回结果集的语句。这包括SELECT语句,不含INTO var_list子句,以及其他语句,如SHOWEXPLAINCHECK TABLE。函数可以通过SELECT ... INTO var_list或使用游标和FETCH语句来处理结果集。请参阅Section 15.2.13.1, “SELECT ... INTO 语句”Section 15.6.6, “Cursors”

  • FLUSH语句。

  • 存储函数不能递归调用。

  • 存储函数或触发器不能修改已经被使用的(读取或写入)语句所使用的表。

  • 如果在存储函数中多次引用临时表,以不同的别名引用,会出现Can't reopen table: 'tbl_name'错误,即使引用在不同的语句中。

  • HANDLER ... READ语句,可以引起复制错误,并且被禁止。

对于触发器,以下额外限制适用:

  • 触发器不受外键操作激活。

  • 在使用行级别复制时,副本上的触发器不受源端语句激活。使用语句级别复制时,副本上的触发器会被激活。更多信息,请参阅Section 19.5.1.36, “Replication and Triggers”

  • 在触发器中不允许使用RETURN语句,因为触发器不能返回值。要退出触发器,可以使用LEAVE语句。

  • 触发器不允许在mysql数据库中使用表,也不允许在INFORMATION_SCHEMAperformance_schema表中使用。那些表实际上是视图,触发器不能在视图上使用。

  • 触发器缓存不检测底层对象的元数据变化。如果触发器使用表,并且该表自触发器加载到缓存中以来已经改变,触发器将使用 outdated元数据。

同一个标识符可能用于存储程序参数、局部变量和表列。同样,同一个局部变量名也可以在嵌套块中使用。例如:

CREATE PROCEDURE p (i INT)
BEGIN
  DECLARE i INT DEFAULT 0;
  SELECT i FROM t;
  BEGIN
    DECLARE i INT DEFAULT 1;
    SELECT i FROM t;
  END;
END;

在这种情况下,标识符是模糊的,以下优先级规则适用:

  • 局部变量优先于存储程序参数或表列。

  • 存储程序参数优先于表列。

  • 在内层块中的局部变量优先于外层块中的局部变量。

变量优先于表列的行为非标准。

复制考虑

使用存储程序可能会导致复制问题。这一问题在第27.7节,“存储程序二进制日志”中有更详细的讨论。

--replicate-wild-do-table=db_name.tbl_name选项适用于表、视图和触发器。但是,不适用于存储程序和函数、事件。要过滤操作这些对象的语句,使用一个或多个--replicate-*-db选项。

没有存储程序调试工具。

MySQL存储程序语法基于SQL:2003标准。以下来自该标准的项目当前不支持:

  • UNDO处理程序

  • FOR循环

为了防止会话之间的交互问题,当客户端发出语句时,服务器使用可执行语句的存储程序和触发器快照。也就是说,服务器计算可能在执行语句时使用的过程、函数和触发器列表,加载它们,然后执行语句。语句执行期间,不会看到其他会话中对存储程序的更改。

为了最大化并发,存储函数应该尽量减少副作用;特别是,更新表中的存储函数可以减少对该表的并发操作。存储函数在执行前锁定表,以避免日志中的不一致由于语句执行顺序和日志顺序的不一致。使用语句基于的二进制日志时,调用函数的语句被记录,而不是语句内部执行的语句。因此,存储函数更新相同的基础表时,不会并发执行。相比之下,存储程序不锁定表。所有语句执行在存储程序中都被写入到二进制日志中,包括语句基于的二进制日志。请参阅第27.7节,“存储程序二进制日志”

以下限制เฉพาะ是Event Scheduler的:

  • 事件名称将以不区分大小写的方式处理。例如,你不能在同一个数据库中拥有两个事件名为anEventAnEvent的事件。

  • 不能在存储程序中创建事件。不能在存储程序中使用变量指定事件名来alter或drop事件。事件也不能创建、alter或drop存储程序或触发器。

  • LOCK TABLES语句生效期间,不允许对事件执行DDL语句。

  • 使用YEARQUARTERMONTHYEAR_MONTH间隔的事件将以月为单位解决;使用其他间隔的事件将以秒为单位解决。由于时间的不确定性、线程应用的性质和创建事件和执行事件的时间都需要一些时间,因此事件可能会延迟1-2秒。但是,信息_schema中的EVENTS表的LAST_EXECUTED列始终是准确的到秒的实际事件执行时间。 (参见Bug #16522。)

  • 事件体内的语句每次执行都在新的连接中执行,因此这些语句在服务器的语句计数中没有影响,如Com_selectCom_insert,这些计数由SHOW STATUS语句显示。然而,这些计数在全局范围内是更新的。 (Bug #16422)

  • 事件不支持后于UnixEpoch的时间,这是大约2038年开始的时间。事件调度器不允许这种日期。 (Bug #16396)

  • CREATE EVENTALTER EVENT语句中的ON SCHEDULE子句中的存储函数、可加载函数和表引用不受支持。这些引用不被允许。 (参见Bug #22830以获取更多信息。)

虽然存储程序、存储函数、触发器和计划事件都支持使用NDB存储引擎,但是你需要注意这些对象在MySQL服务器之间不自动传播。这是因为存储程序和触发器定义在mysql系统数据库中的InnoDB表中,这些表不在Cluster节点之间复制。

任何与 MySQL 集群表进行交互的存储程序或触发器都必须重新创建,通过在每个 MySQL 服务器上运行相应的CREATE PROCEDURECREATE FUNCTIONCREATE TRIGGER语句。类似地,对现有存储程序或触发器的更改必须在所有集群 SQL 节点上明确地使用适当的ALTERDROP语句进行。

Warning

不要尝试通过将任何mysql数据库表转换为使用NDB存储引擎来解决问题。修改系统表在mysql数据库中不是支持的,并且可能会产生不良结果。