MySQL 8.4 参考手册  /  存储对象  /  存储程序的限制

27.8 存储程序的限制

这些限制适用于第 27 章,“存储对象”中描述的功能。

这里提到的一些限制适用于所有存储例程;也就是说,既适用于存储过程,也适用于存储函数。还有一些特定于存储函数而不适用于存储过程的限制

存储函数的限制也适用于触发器。还有一些特定于触发器的限制

存储过程的限制也适用于事件调度程序事件定义的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 供应商可以决定是否允许使用它们。

  • 返回结果集的语句。这包括没有INTO var_list子句的SELECT语句,以及其他语句,例如SHOWEXPLAINCHECK TABLE。函数可以使用SELECT ... INTO var_list或使用游标和FETCH语句处理结果集。请参见第 15.2.13.1 节,“SELECT ... INTO 语句”第 15.6.6 节,“游标”

  • FLUSH语句。

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

  • 存储函数或触发器不能修改调用该函数或触发器的语句已在使用(用于读取或写入)的表。

  • 如果在存储函数中使用不同的别名多次引用同一个临时表,则会发生Can't reopen table: 'tbl_name'错误,即使这些引用发生在函数的不同语句中。

  • HANDLER ... READ语句调用存储函数会导致复制错误,因此不允许使用。

触发器的限制

对于触发器,适用以下附加限制

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

  • 使用基于行的复制时,副本上的触发器不会被源上发起的语句激活。使用基于语句的复制时,副本上的触发器会被激活。有关更多信息,请参见第 19.5.1.36 节,“复制和触发器”

  • 触发器中不允许使用RETURN语句,因为它不能返回值。要立即退出触发器,请使用LEAVE语句。

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

  • 当底层对象的元数据发生更改时,触发器缓存不会检测到。如果触发器使用了一个表,并且该表自触发器加载到缓存后发生了更改,则触发器将使用过时的元数据进行操作。

存储例程中的名称冲突

同一个标识符可以用于例程参数、局部变量和表列。此外,相同的局部变量名可以在嵌套块中使用。例如

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 选项。

调试注意事项

没有存储例程调试工具。

SQL:2003 标准中不支持的语法

MySQL 存储例程语法基于 SQL:2003 标准。 目前不支持该标准中的以下各项

  • UNDO 处理程序

  • FOR 循环

存储例程并发注意事项

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

为了实现最大并发性,存储函数应尽量减少其副作用; 特别是,在存储函数中更新表会减少对该表的并发操作。 存储函数在执行之前会获取表锁,以避免由于语句执行顺序和它们在日志中出现的顺序不匹配而导致二进制日志不一致。 使用基于语句的二进制日志记录时,会记录调用函数的语句,而不是函数中执行的语句。 因此,更新相同基础表的存储函数不会并行执行。 相比之下,存储过程不会获取表级锁。 即使对于基于语句的二进制日志记录,在存储过程中执行的所有语句都会写入二进制日志。 请参阅 第 27.7 节“存储程序二进制日志记录”

事件调度程序限制

以下限制特定于事件调度程序

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

  • 事件不能在存储程序中创建。 如果事件名称是通过变量指定的,则不能在存储程序中更改或删除事件。 事件也不能创建、更改或删除存储例程或触发器。

  • LOCK TABLES 语句生效时,禁止对事件执行 DDL 语句。

  • 使用间隔 YEARQUARTERMONTHYEAR_MONTH 的事件计时以月为单位解析; 使用任何其他间隔的事件以秒为单位解析。 无法使计划在同一秒发生的事件按给定顺序执行。 此外,由于舍入、线程应用程序的性质以及创建事件和发出执行信号需要非零时间长度,因此事件可能会延迟 1 到 2 秒。 但是,信息架构 EVENTS 表的 LAST_EXECUTED 列中显示的时间始终精确到实际事件执行时间的 1 秒内。 (另请参阅错误 #16522。)

  • 事件主体中包含的语句的每次执行都在一个新的连接中进行; 因此,这些语句在给定用户会话中对服务器的语句计数(例如 SHOW STATUS 显示的 Com_selectCom_insert)没有影响。 但是,此类计数在全局范围内更新。 (错误 #16422)

  • 事件不支持晚于 Unix 纪元结束的时间; 这大约是 2038 年年初。 事件调度程序明确禁止使用此类日期。 (错误 #16396)

  • 不支持在 CREATE EVENTALTER EVENT 语句的 ON SCHEDULE 子句中引用存储函数、可加载函数和表。 不允许此类引用。 (有关详细信息,请参阅错误 #22830。)

NDB 集群中的存储例程和触发器

虽然使用 NDB 存储引擎的表支持存储过程、存储函数、触发器和计划事件,但您必须记住,这些不会在充当集群 SQL 节点的 MySQL 服务器之间自动传播。 这是因为存储例程和触发器定义存储在使用 InnoDB 表的 mysql 系统数据库中的表中,而这些表不会在集群节点之间复制。

任何与 MySQL 集群表交互的存储例程或触发器都必须通过在参与集群的每台 MySQL 服务器(您希望在其中使用该存储例程或触发器)上运行相应的 CREATE PROCEDURECREATE FUNCTIONCREATE TRIGGER 语句来重新创建。 同样,对现有存储例程或触发器的任何更改都必须在所有集群 SQL 节点上明确执行,方法是在访问集群的每台 MySQL 服务器上使用相应的 ALTERDROP 语句。

警告

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