这些限制适用于 第 27 章:存储对象 中描述的功能。
此处指出的一些限制适用于所有存储例程;也就是说,适用于存储过程和存储函数。还有一些 特定于存储函数而不适用于存储过程的限制。
存储函数的限制也适用于触发器。还有一些 特定于触发器的限制。
存储过程的限制也适用于事件调度程序事件定义的 DO
子句。还有一些 特定于事件的限制。
存储例程不能包含任意 SQL 语句。以下语句是不允许的
锁定语句
LOCK TABLES
和UNLOCK TABLES
。SQL 预处理语句(
PREPARE
、EXECUTE
、DEALLOCATE PREPARE
)可以在存储过程中使用,但不能在存储函数或触发器中使用。因此,存储函数和触发器不能使用动态 SQL(将语句构造为字符串,然后执行它们)。通常,在 SQL 预处理语句中不允许使用的语句在存储程序中也不允许使用。有关作为预处理语句支持的语句列表,请参阅 第 15.5 节:“预处理语句”。例外情况是
SIGNAL
、RESIGNAL
和GET DIAGNOSTICS
,它们在预处理语句中不允许使用,但在存储程序中允许使用。因为局部变量仅在存储程序执行期间有效,所以不允许在存储程序中创建的预处理语句中引用它们。预处理语句的作用域是当前会话,而不是存储程序,因此语句可以在程序结束后执行,此时变量将不再有效。例如,
SELECT ... INTO
不能用作预处理语句。此限制也适用于存储过程和函数参数。请参阅 第 15.5.1 节:“PREPARE 语句”。local_var
在所有存储程序(存储过程和函数、触发器和事件)中,解析器将
BEGIN [WORK]
视为BEGIN ... END
块的开头。要在这种情况下开始事务,请使用START TRANSACTION
。
以下附加语句或操作在存储函数中不允许使用。它们在存储过程中允许使用,但从存储函数或触发器内部调用的存储过程除外。例如,如果您在存储过程中使用 FLUSH
,则不能从存储函数或触发器调用该存储过程。
执行显式或隐式提交或回滚的语句。SQL 标准不要求支持这些语句,该标准指出每个 DBMS 供应商可以自行决定是否允许它们。
返回结果集的语句。这包括没有
INTO
子句的var_list
SELECT
语句以及其他语句,例如SHOW
、EXPLAIN
和CHECK 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.37 节:“复制和触发器”。
不允许在
mysql
数据库中的表上使用触发器。也不允许在INFORMATION_SCHEMA
或performance_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.8 节“存储程序二进制日志记录”中进行了进一步讨论。
--replicate-wild-do-table=
选项适用于表、视图和触发器。它不适用于存储过程和函数或事件。要过滤对后者对象进行操作的语句,请使用一个或多个 db_name.tbl_name
--replicate-*-db
选项。
为了防止会话之间交互的问题,当客户端发出语句时,服务器会使用可用于执行该语句的例程和触发器的快照。也就是说,服务器会计算一个可能在语句执行期间使用的过程、函数和触发器的列表,加载它们,然后继续执行语句。当语句执行时,它不会看到其他会话对例程所做的更改。
为了获得最大并发性,存储函数应尽量减少其副作用;特别是,在存储函数中更新表会减少对该表的并发操作。存储函数在执行之前会获取表锁,以避免由于语句执行顺序与其在日志中出现的顺序不匹配而导致二进制日志不一致。当使用基于语句的二进制日志记录时,会记录调用函数的语句,而不是在函数中执行的语句。因此,更新相同基础表的存储函数不会并行执行。相反,存储过程不会获取表级锁。在存储过程中执行的所有语句都会写入二进制日志,即使对于基于语句的二进制日志记录也是如此。请参阅第 27.8 节“存储程序二进制日志记录”。
以下限制特定于事件调度程序
事件名称以不区分大小写的方式处理。例如,您不能在同一个数据库中拥有两个名称分别为
anEvent
和AnEvent
的事件。事件不能在存储程序中创建。如果事件名称是通过变量指定的,则不能在存储程序中更改或删除事件。事件也不能创建、更改或删除存储例程或触发器。
当
LOCK TABLES
语句生效时,禁止对事件执行 DDL 语句。使用间隔
YEAR
、QUARTER
、MONTH
和YEAR_MONTH
的事件时间以月为单位解析;使用任何其他间隔的事件时间以秒为单位解析。无法使计划在同一秒发生的事件按给定顺序执行。此外,由于舍入、线程应用程序的性质以及创建事件和发出执行信号所需的时间长度不为零,事件可能会延迟 1 到 2 秒。但是,Information SchemaEVENTS
表的LAST_EXECUTED
列中显示的时间始终精确到实际事件执行时间的 1 秒以内。(另请参阅错误 #16522。)事件正文中包含的语句的每次执行都在一个新连接中进行;因此,这些语句在给定用户会话中对服务器的语句计数(例如
Com_select
和Com_insert
,由SHOW STATUS
显示)没有影响。但是,此类计数会在全局范围内更新。(错误 #16422)事件不支持晚于 Unix 纪元结束的时间;这大约是 2038 年初。事件调度程序明确不允许此类日期。(错误 #16396)
不支持在
CREATE EVENT
和ALTER EVENT
语句的ON SCHEDULE
子句中引用存储函数、可加载函数和表。不允许此类引用。(有关更多信息,请参阅错误 #22830。)
虽然使用 NDB
存储引擎的表支持存储过程、存储函数、触发器和计划事件,但您必须记住,这些不会在充当集群 SQL 节点的 MySQL 服务器之间自动传播。这是因为存储例程和触发器定义存储在 mysql
系统数据库中使用 InnoDB
表的表中,而这些表不会在集群节点之间复制。
任何与 MySQL Cluster 表交互的存储例程或触发器都必须通过在参与集群的每个 MySQL 服务器(您希望在其中使用存储例程或触发器)上运行相应的 CREATE PROCEDURE
、CREATE FUNCTION
或 CREATE TRIGGER
语句来重新创建。同样,对现有存储例程或触发器的任何更改都必须在所有集群 SQL 节点上显式执行,方法是在访问集群的每个 MySQL 服务器上使用相应的 ALTER
或 DROP
语句。
不要 尝试通过将任何 mysql
数据库表转换为使用 NDB
存储引擎来解决上述问题。不支持更改 mysql
数据库中的系统表,并且很可能会产生 undesirable results。