## 25.8 存储程序的限制

[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)

-   [存储例程中不允许使用 SQL 语句](stored-program-restrictions.html#stored-routine-sql-restrictions)

-   [存储函数的限制](stored-program-restrictions.html#stored-routines-function-restrictions)

-   [触发器的限制](stored-program-restrictions.html#stored-routines-trigger-restrictions)

-   [存储例程中的名称冲突](stored-program-restrictions.html#stored-routine-name-conflicts)

-   [复制注意事项](stored-program-restrictions.html#stored-routines-replication-restrictions)

-   [调试注意事项](stored-program-restrictions.html#stored-routines-debugging-restrictions)

-   [SQL:2003 标准中不支持的语法](stored-program-restrictions.html#stored-routines-standard-restrictions)

-   [存储例程并发注意事项](stored-program-restrictions.html#stored-routines-concurrency-restrictions)

-   [事件调度程序限制](stored-program-restrictions.html#stored-routines-event-restrictions)

-   [NDB Cluster 中的存储例程和触发器](stored-program-restrictions.html#stored-routines-ndbcluster)

    这些限制适用于中描述的功能[第 25 章，*存储对象*](stored-objects.html).

    这里提到的一些限制适用于所有存储的例程；也就是说，对于存储过程和存储函数。还有一些[特定于存储函数的限制](stored-program-restrictions.html#stored-routines-function-restrictions)但不是存储过程。

    存储函数的限制也适用于触发器。还有一些[特定于触发器的限制](stored-program-restrictions.html#stored-routines-trigger-restrictions).

    存储过程的限制也适用于[`做`](do.html)Event Scheduler 事件定义的子句。还有一些[特定于事件的限制](stored-program-restrictions.html#stored-routines-event-restrictions).

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

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

-   锁定语句[`锁定表`](lock-tables.html)和[`解锁桌子`](lock-tables.html).

-   [`改变视图`](alter-view.html).

-   [`加载数据`](load-data.html)和[`加载 XML`](load-xml.html).

-   SQL 准备语句 ([`准备`](prepare.html),[`执行`](execute.html),[`解除分配准备`](deallocate-prepare.html)) 可用于存储过程，但不能用于存储函数或触发器。因此，存储函数和触发器不能使用动态 SQL（您将语句构造为字符串然后执行它们）。

-   通常，SQL 准备语句中不允许的语句在存储程序中也不允许。有关支持作为准备好的语句的语句列表，请参阅[第 13.5 节，“准备好的陈述”](sql-prepared-statements.html).例外是[`信号`](signal.html),[`重新信号`](resignal.html)， 和[`得到诊断`](get-diagnostics.html), 不允许作为准备好的语句，但在存储程序中是允许的。

-   因为局部变量仅在存储程序执行期间才在作用域内，所以在存储程序中创建的准备好的语句中不允许引用它们。准备好的语句作用域是当前会话，而不是存储的程序，因此该语句可以在程序结束后执行，此时变量将不再在作用域内。例如，`选择 ... 进入 *`local_var`*`不能用作准备好的语句。此限制也适用于存储过程和函数参数。看[第 13.5.1 节，“准备声明”](prepare.html).

-   在所有存储程序（存储过程和函数、触发器和事件）中，解析器将[`开始[工作]`](commit.html)作为一个开始[`开始...结束`](begin-end.html)堵塞。要在此上下文中开始事务，请使用[`开始交易`](commit.html)反而。

### 存储函数的限制

存储函数中不允许使用以下附加语句或操作。它们在存储过程中是允许的，但从存储函数或触发器中调用的存储过程除外。例如，如果您使用[`冲洗`](flush.html)在存储过程中，不能从存储函数或触发器调用该存储过程。

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

-   返回结果集的语句。这包括[`选择`](select.html)没有`进入 *`var_list`*`子句和其他语句，例如[`显示`](show.html),[`解释`](explain.html)， 和[`检查表`](check-table.html).一个函数可以处理一个结果集[`选择 ... 进入 *`var_list`*`](select-into.html)或通过使用光标和[`拿来`](fetch.html)陈述。看[第 13.2.10.1 节，“SELECT ... INTO 语句”](select-into.html)， 和[第 13.6.6 节，“光标”](cursors.html).

-   [`冲洗`](flush.html)陈述。

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

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

-   如果您在不同别名下的存储函数中多次引用临时表，则`无法重新打开表：'*`tbl_name```*``'```即使引用出现在函数内的不同语句中，也会发生错误。

-   [`处理程序...阅读`](handler.html)调用存储函数的语句可能会导致复制错误并且是不允许的。

### 触发器的限制

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

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

-   使用基于行的复制时，副本上的触发器不会被源自源的语句激活。使用基于语句的复制时，副本上的触发器会被激活。有关详细信息，请参阅[第 17.5.1.36 节，“复制和触发器”](replication-features-triggers.html).

-   这[`返回`](return.html)触发器中不允许使用语句，它不能返回值。要立即退出触发器，请使用[`离开`](leave.html)陈述。

-   不允许在表中使用触发器`mysql`数据库。他们也不允许`INFORMATION_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;
```

在这种情况下，标识符不明确，适用以下优先规则：

-   局部变量优先于常规参数或表列。

-   例程参数优先于表列。

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

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

### 复制注意事项

使用存储的例程可能会导致复制问题。这个问题在中进一步讨论[第 25.7 节，“存储程序二进制日志”](stored-programs-logging.html).

这[`--replicate-wild-do-table=*`db_name.tbl_name`*`](replication-options-replica.html#option_mysqld_replicate-wild-do-table)选项适用于表、视图和触发器。它不适用于存储过程和函数或事件。要过滤对后者对象操作的语句，请使用一个或多个`--replicate-*-db`选项。

### 调试注意事项

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

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

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

-   `撤消`处理程序

-   `为了`循环

### 存储例程并发注意事项

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

为了获得最大的并发性，存储函数应该尽量减少它们的副作用；特别是，在存储函数中更新表可以减少对该表的并发操作。存储函数在执行前获取表锁，以避免由于语句执行顺序和出现在日志中的时间不匹配而导致二进制日志不一致。当使用基于语句的二进制日志时，会记录调用函数的语句，而不是函数内执行的语句。因此，更新相同基础表的存储函数不会并行执行。相反，存储过程不获取表级锁。在存储过程中执行的所有语句都被写入二进制日志，即使是基于语句的二进制日志。看[第 25.7 节，“存储程序二进制日志”](stored-programs-logging.html).

### 事件调度程序限制

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

-   事件名称以不区分大小写的方式处理。例如，您不能在同一个数据库中有两个事件的名称`一个事件`和`一个事件`.

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

-   禁止对事件的 DDL 语句，而[`锁定表`](lock-tables.html)声明生效。

-   使用间隔的事件计时`年`,`25美分硬币`,`月`和`年/月`在几个月内解决；使用任何其他时间间隔的问题都会在几秒钟内得到解决。无法使计划在同一秒发生的事件按给定顺序执行。此外，由于四舍五入、线程化应用程序的性质以及创建事件并向其执行事件发送信号所需的非零时间长度可能会延迟1或2秒。然而，图中显示的时间[`信息模式。事件`](information-schema-events-table.html)桌子的`最后一次处决`列始终精确到实际事件执行时间的1秒以内。（另请参见Bug#16522。）

-   事件主体中包含的语句的每次执行都发生在一个新的连接中；因此，在给定的用户会话中，这些语句对服务器的语句计数没有影响，例如`Com_select`和`Com_插入`由[`显示状态`](show-status.html).然而，这些都很重要*是*在全球范围内更新。（Bug#16422）

-   事件不支持晚于Unix时代结束的时间；这大约是2038年的开始。事件计划程序特别不允许此类日期。（错误#16396）

-   中存储函数、可加载函数和表的引用`按时`条款[`创建事件`](create-event.html)和[`改变事件`](alter-event.html)语句不受支持。这些类型的引用是不允许的。（有关更多信息，请参见Bug#22830。）

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

而使用[`NDB`](mysql-cluster.html)存储引擎，你必须记住这些*不*在充当群集SQL节点的MySQL服务器之间自动传播。这是因为存储的例程和触发器定义存储在`mysql`系统数据库使用`InnoDB`不在群集节点之间复制的表。

任何与MySQL集群表交互的存储例程或触发器都必须通过运行适当的[`创建过程`](create-procedure.html), [`创建函数`](create-function.html)或[`创建触发器`](create-trigger.html)在您希望使用存储的例程或触发器的集群中，每个MySQL服务器上的语句。类似地，对现有存储例程或触发器的任何更改都必须使用适当的`改变`或`滴`访问集群的每个MySQL服务器上的语句。

警告

做*不*尝试通过转换任何`mysql`要使用[`NDB`](mysql-cluster.html)存储引擎。*更改系统中的系统表`mysql`数据库不受支持*而且很可能产生不理想的结果。
