MySQL 8.0 参考手册  / 第25章存储对象  /  23.8 存储程序的限制

23.8 存储程序的限制

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

此处提到的一些限制适用于所有存储例程;也就是说,存储过程和存储函数。还有一些 特定于存储函数而非存储过程的限制。

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

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

存储例程中不允许的 SQL 语句

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

  • 锁定语句LOCK TABLESUNLOCK TABLES.

  • ALTER VIEW.

  • LOAD DATALOAD XML

  • SQL 预处理语句 ( PREPARE, EXECUTE, DEALLOCATE PREPARE) 可用于存储过程,但不能用于存储函数或触发器。因此,存储函数和触发器不能使用动态 SQL(您将语句构造为字符串,然后执行它们)。

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

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

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

存储函数的限制

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

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

  • 返回结果集的语句。这包括 SELECT没有子句的语句和其他语句,例如 、 和 。函数可以使用 或通过使用游标和语句来处理结果集。请参阅第 13.2.9.1 节,“SELECT ... INTO 语句”第 13.6.6 节,“游标”INTO var_listSHOWEXPLAINCHECK TABLESELECT ... INTO var_listFETCH

  • FLUSH声明。

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

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

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

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

触发器的限制

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

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

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

  • 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;

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

  • 局部变量优先于例程参数或表列。

  • 例程参数优先于表列。

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

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

复制注意事项

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

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

调试注意事项

没有存储的例程调试工具。

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

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

  • UNDO处理程序

  • FOR循环

存储例程并发注意事项

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

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

事件调度程序限制

以下限制特定于 Event Scheduler:

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

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

  • LOCK TABLES当声明生效 时,禁止对事件进行 DDL 声明 。

  • YEAR使用间隔、 QUARTERMONTH和 的 事件计时YEAR_MONTH以月为单位解决;那些使用任何其他时间间隔的问题会在几秒钟内解决。无法使计划在同一秒发生的事件按给定顺序执行。此外,由于四舍五入、线程应用程序的性质,以及创建事件和发出执行信号所需的非零时间长度这一事实,事件可能会延迟多达 1 或 2 秒。但是, 表列或 表 INFORMATION_SCHEMA.EVENTS 的列中显示的时间LAST_EXECUTEDmysql.eventlast_executed列始终精确到实际事件执行时间的一秒以内。(另请参见缺陷 #16522。)

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

  • 事件不支持晚于 Unix Epoch 结束的时间;这大约是 2038 年的开始。事件调度器明确不允许这样的日期。(漏洞 #16396)

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

NDB Cluster 中的存储程序

虽然使用存储引擎的表都支持存储过程、存储函数、触发器和计划事件 NDB,但您必须记住,这些不会在充当 Cluster SQL 节点的 MySQL 服务器之间自动传播。这是因为以下原因:

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

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

任何与 NDB Cluster 表交互的存储例程或触发器都必须通过在每个参与您希望使用存储例程或触发器的集群的 MySQL 服务器上运行适当CREATE PROCEDURECREATE FUNCTION、或 语句 来重新创建 。CREATE TRIGGER同样,对现有存储例程或触发器的任何更改都必须在所有 Cluster SQL 节点上显式执行,在 访问集群的每个 MySQL 服务器上 使用适当的ALTER或语句。DROP

警告

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