这些限制适用于第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供应商可以决定是否允许它们。
-
返回结果集的语句。这包括
SELECT
语句,除非它们具有INTO
子句,以及其他语句,如var_list
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.36 节,“复制和触发器”。
-
不允许在
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.7 节,“存储程序二进制日志记录”。
--replicate-wild-do-table=
选项适用于表、视图和触发器。它不适用于存储过程和函数、事件。要过滤操作这些对象的语句,请使用一个或多个 db_name.tbl_name
--replicate-*-db
选项。
为了防止会话之间的交互问题,当客户端发出语句时,服务器使用可执行语句的快照来加载存储过程和触发器。也就是说,服务器计算出可能用于执行语句的过程、函数和触发器的列表,加载它们,然后执行语句。在语句执行期间,它不看到其他会话对存储过程的更改。
为了最大化并发,存储函数应该尽量减少副作用;特别是,在存储函数中更新表可以减少该表上的并发操作。存储函数在执行之前会获取表锁,以避免二进制日志中的不一致由于语句执行顺序和日志中的顺序不匹配。当使用基于语句的二进制日志记录时,记录的是调用函数的语句,而不是函数内部执行的语句。因此,更新相同基础表的存储函数不会并行执行。相比之下,存储过程不获取表级锁。存储过程中的所有语句都会被写入二进制日志,即使使用基于语句的二进制日志记录。参见第 27.7 节,“存储程序二进制日志记录”。
事件计划器有以下限制:
-
事件名称以不区分大小写方式处理。例如,您不能在同一个数据库中拥有两个事件,名称分别为
anEvent
和AnEvent
。 -
不能从存储程序中创建事件。也不能从存储程序中更改或删除事件,如果事件名称是通过变量指定的。事件也不能创建、更改或删除存储例程或触发器。
-
在
LOCK TABLES
语句生效时,不允许执行事件的 DDL 语句。 -
使用间隔
YEAR
、QUARTER
、MONTH
和YEAR_MONTH
的事件定时将以月份为单位解析;使用其他间隔的事件定时将以秒为单位解析。无法确保在同一秒内执行的事件的执行顺序。此外,由于线程应用程序的性质和创建事件和信号执行所需的非零时间长度,事件可能会延迟 1 或 2 秒。但是,信息模式EVENTS
表的LAST_EXECUTED
列的时间总是准确到事件执行时间的一秒内。(参见 Bug #16522。) -
事件体中的每个语句执行都将在新的连接中进行;因此,这些语句不会影响服务器的语句计数,例如
Com_select
和Com_insert
,这些计数由SHOW STATUS
显示。然而,这些计数 将 在全局范围内更新。(Bug #16422) -
事件不支持 Unix 纪元结束后的日期;这大约是 2038 年初。这些日期特定不被事件计划器所允许。(Bug #16396)
-
在
CREATE EVENT
和ALTER EVENT
语句的ON SCHEDULE
子句中,不支持对存储函数、可加载函数和表的引用。(参见 Bug #22830 以获取更多信息。)
虽然存储过程、存储函数、触发器和计划事件都支持使用 NDB
存储引擎的表,但是您必须注意,这些定义不会自动在 MySQL 服务器之间传播,因为这些定义存储在 mysql
系统数据库的 InnoDB
表中,这些表不会在集群节点之间复制。
任何与 MySQL 集群表交互的存储例程或触发器必须通过在每个参与集群的 MySQL 服务器上运行适当的 CREATE PROCEDURE
、CREATE FUNCTION
或 CREATE TRIGGER
语句来重新创建。同样,对现有存储例程或触发器的任何更改都必须在所有集群 SQL 节点上明确地使用适当的 ALTER
或 DROP
语句。
请 不要 试图绕过上述问题,方法是将任何 mysql
数据库表转换为使用 NDB
存储引擎。修改 mysql 数据库中的系统表是不支持的,并且很可能会产生不良的结果。