23.8 对存储程序的限制

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

此处指出的某些限制适用于所有存储的例程。也就是存储过程和存储函数。也有一些特定于存储功能的限制,但不是存储过程。

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

对存储过程的限制也适用于 Event Scheduler 事件定义的DO子句。还有一些特定于事件的限制

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

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

  • 锁定语句LOCK TABLESUNLOCK TABLES

  • ALTER VIEW.

  • LOAD DATALOAD TABLE

  • SQL 预准备语句(PREPAREEXECUTEDEALLOCATE PREPARE)可以在存储过程中使用,但不能在存储函数或触发器中使用。因此,存储的函数和触发器不能使用动态 SQL(将语句构造为字符串然后执行它们)。

  • 通常,SQL 预准备语句中不允许的语句在存储程序中也不允许。有关作为预备语句支持的语句列 table,请参见第 13.5 节“准备好的语句”SIGNALRESIGNALGET DIAGNOSTICS除外,它们是不允许的预备语句,但在存储程序中是允许的。

  • 因为局部变量仅在存储程序执行期间才是作用域,所以在存储程序内创建的准备好的语句中不允许引用它们。准备好的语句作用域是当前会话,而不是存储的程序,因此该语句可以在程序结束后执行,此时变量将不再在作用域内。例如,SELECT ... INTO local_var不能用作预备语句。此限制也适用于存储过程和函数参数。参见第 13.5.1 节“ PREPARE 语句”

  • 在所有存储的程序(存储的过程和函数,触发器和事件)中,解析器将BEGIN [WORK]视为开始...结束块的开始。要在这种情况下开始事务,请改用START TRANSACTION

对存储功能的限制

存储的函数中不允许以下附加语句或操作。它们在存储过程中是允许的,但从存储函数或触发器中调用的存储过程除外。例如,如果在存储过程中使用FLUSH,则不能从存储函数或触发器中调用该存储过程。

  • 执行显式或隐式提交或回滚的语句。 SQL 标准不需要支持这些语句,该标准指出每个 DBMS 供应商都可以决定是否允许它们。

  • 返回结果集的语句。这包括不具有INTO var_list子句的SELECT语句和其他语句,例如SHOWEXPLAINCHECK TABLE。函数可以使用选择... INTO var_list或使用游标和FETCH语句来处理结果集。参见第 13.2.9.1 节“ SELECT ... INTO 语句”第 13.6.6 节“游标”

  • FLUSH statements.

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

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

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

  • 处理...阅读调用存储函数的语句可能会导致复制错误,因此是不允许的。

触发器限制

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

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

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

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

  • mysql数据库中的 table 不允许使用触发器。也不允许在INFORMATION_SCHEMAperformance_schematable 上使用它们。这些 table 实际上是视图,并且不允许在视图上使用触发器。

  • 触发器缓存不会检测基础对象的元数据何时更改。如果触发器使用 table 并且自将触发器加载到缓存以来该 table 已更改,则触发器将使用过时的元数据进行操作。

存储例程中的名称冲突

相同的标识符可能用于例程参数,局部变量和 table 列。同样,可以在嵌套块中使用相同的局部变量名称。例如:

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;

在这种情况下,标识符不明确,并且适用以下优先级规则:

  • 局部变量优先于常规参数或 table 列。

  • 例程参数优先于 table 列。

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

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

Replication Considerations

使用存储的例程可能会导致复制问题。 第 23.7 节“存储的程序二进制日志”中将进一步讨论此问题。

--replicate-wild-do-table=db_name.tbl_name选项适用于 table,视图和触发器。它不适用于存储过程和功能或事件。要过滤对后一个对象操作的语句,请使用一个或多个--replicate-*-db选项。

Debugging Considerations

没有存储的例行调试功能。

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

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

  • UNDO个处理程序

  • FOR个循环

存储的常规并发注意事项

为了防止会话之间的交互问题,当 Client 端发出一条语句时,服务器使用例程快照和可用于执行该语句的触发器。即,服务器计算在语句执行期间可能使用的过程,函数和触发器的列 table,加载它们,然后 continue 执行该语句。语句执行时,看不到其他会话对例程执行的更改。

为了获得最大的并发性,存储函数应将其副作用降到最低。特别是,更新存储功能中的 table 可以减少对该 table 的并发操作。存储的函数在执行之前获取 table 锁,以避免二进制日志由于语句执行 Sequences 和语句出现在日志中的 Sequences 不匹配而导致不一致。使用基于语句的二进制日志记录时,将记录调用函数的语句,而不是记录在函数内执行的语句。因此,更新相同基础 table 的存储函数不会并行执行。相反,存储过程不获取 table 级锁。在存储过程中执行的所有语句都被写入二进制日志,即使对于基于语句的二进制日志也是如此。参见第 23.7 节“存储的程序二进制日志”

事件计划程序限制

以下限制特定于事件计划程序:

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

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

  • LOCK TABLES语句生效时,禁止使用有关事件的 DDL 语句。

  • 使用间隔YEARQUARTERMONTHYEAR_MONTH的事件时间以月为单位进行解析;那些使用其他间隔的时间以秒为单位进行解析。无法使计划在同一秒发生的事件以给定 Sequences 执行。此外,由于四舍五入,线程应用程序的性质以及创建事件和 table 示其执行需要非零时间长度的事实,事件可能会延迟多达 1 或 2 秒。但是,在INFORMATION_SCHEMA.EVENTStable 的LAST_EXECUTED列或mysql.eventtable 的last_executed列中显示的时间始终精确到实际事件执行时间的一秒以内。 (另请参见 Bug#16522.)

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

  • 事件的支持时间不晚于 Unix Epoch 的结束。这大约是 2038 年的年初。事件计划程序明确不允许这样的日期。错误 16396)

  • 不支持在CREATE EVENTALTER EVENT语句的ON SCHEDULE子句中引用存储的函数,用户定义的函数和 table。不允许使用此类参考。 (有关更多信息,请参见 Bug#22830.)

存储在 NDB Cluster 中的例程和触发器. 使用NDB存储引擎的 table 均支持存储过程,存储函数和触发器;但是,请务必记住,它们不会在充当群集 SQL 节点的 MySQL 服务器之间自动传播。这是由于以下原因:

  • 使用MyISAM存储引擎将存储的例程定义保存在mysql系统数据库的 table 中,因此不参与集群。

  • NDB存储引擎不会读取包含触发器定义的.TRN.TRG文件,也不会在群集节点之间复制它们。

与 NDB 群集 table 交互的任何存储例程或触发器都必须通过在要使用该存储例程或触发器的群集中的每个 MySQL Server 上运行相应的CREATE PROCEDURECREATE FUNCTIONCREATE TRIGGER语句来重新创建。同样,必须在所有 Cluster SQL 节点上显式地执行对现有存储例程或触发器的任何更改,并在每个访问群集的 MySQL Server 上使用适当的ALTERDROP语句。

Warning

不要尝试通过将任何mysql数据库 table 转换为使用NDB存储引擎来解决前面提到的第一项中描述的问题。 *不支持更改mysql数据库中的系统 table,并且很可能产生不希望的结果。