CREATE
[DEFINER = user]
PROCEDURE sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body
CREATE
[DEFINER = user]
FUNCTION sp_name ([func_parameter[,...]])
RETURNS type
[characteristic ...] routine_body
proc_parameter:
[ IN | OUT | INOUT ] param_name type
func_parameter:
param_name type
type:
Any valid MySQL data type
characteristic: {
COMMENT 'string'
| LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
}
routine_body:
Valid SQL routine statement
这些语句用于创建存储例程(存储过程或函数)。也就是说,指定的例程对服务器来说是已知的。默认情况下,存储例程与默认数据库相关联。要将例程与给定数据库显式关联,请指定
db_name.sp_name创建它时的名称。
该CREATE FUNCTION语句还用于 MySQL 以支持可加载函数。请参阅
第 13.7.3.1 节,“可加载函数的 CREATE FUNCTION 语句”。可加载函数可以被视为外部存储函数。存储函数与可加载函数共享它们的命名空间。有关描述服务器如何解释对不同类型函数的引用的规则,
请参见
第 9.2.5 节,“函数名称解析和解析” 。
要调用存储过程,请使用
CALL语句(请参阅
第 13.2.1 节,“CALL 语句”)。要调用存储函数,请在表达式中引用它。该函数在表达式计算期间返回一个值。
CREATE PROCEDURE并
CREATE FUNCTION需要
CREATE ROUTINE特权。如果
DEFINER存在该子句,所需的权限取决于user值,如第 23.6 节“存储对象访问控制”中所述。如果启用了二进制日志记录,CREATE FUNCTION
可能需要SUPER权限,如第 23.7 节“存储程序二进制日志记录”中所述。
默认情况下,MySQL 自动授予
例程创建者权限ALTER ROUTINE和
权限。EXECUTE可以通过禁用
automatic_sp_privileges系统变量来更改此行为。请参阅第 23.2.2 节,“存储例程和 MySQL 权限”。
DEFINER和SQL SECURITY
子句指定在例程执行时检查访问权限时要使用的安全上下文,如本节后面所述
。
如果例程名称与内置 SQL 函数的名称相同,则会出现语法错误,除非您在定义例程或稍后调用例程时在名称和后面的括号之间使用空格。因此,请避免将现有 SQL 函数的名称用于您自己的存储例程。
SQL 模式适用于IGNORE_SPACE内置函数,不适用于存储例程。无论是否
IGNORE_SPACE启用,存储例程名称后始终允许有空格。
括号内的参数列表必须始终存在。如果没有参数,
()则应使用空参数列表。参数名称不区分大小写。
默认情况下,每个参数都是一个IN参数。要为参数指定其他方式,请
在参数名称之前
使用关键字OUT或。INOUT
将参数指定为IN、
OUT或INOUT仅对PROCEDURE. 对于 a
FUNCTION,参数始终被视为
IN参数。
参数将IN值传递给过程。过程可能会修改该值,但当过程返回时,调用者看不到修改。参数将过程中的OUT值传递回调用者。它的初始值
NULL在过程中,当过程返回时,它的值对调用者可见。参数由
INOUT调用者初始化,可以由过程修改,过程返回时调用者可以看到过程所做的任何更改。
对于每个OUT或INOUT
参数,在
CALL调用该过程的语句中传递一个用户定义的变量,以便您可以在该过程返回时获取它的值。如果您从另一个存储过程或函数中调用该过程,您还可以将例程参数或局部例程变量作为OUT
orINOUT参数传递。如果您从触发器中调用该过程,您还可以
作为
or参数传递。
NEW.col_nameOUTINOUT
有关未处理条件对过程参数的影响的信息,请参阅 第 13.6.7.8 节,“条件处理和 OUT 或 INOUT 参数”。
在例程内准备的语句中不能引用例程参数;参见 第 23.8 节,“存储程序的限制”。
以下示例显示了一个简单的存储过程,在给定国家/地区代码的情况下,计算数据库city表中
出现的该国家/地区的城市数。world国家代码使用参数传递IN,城市计数使用OUT参数返回:
mysql> delimiter //
mysql> CREATE PROCEDURE citycount (IN country CHAR(3), OUT cities INT)
BEGIN
SELECT COUNT(*) INTO cities FROM world.city
WHERE CountryCode = country;
END//
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql> CALL citycount('JPN', @cities); -- cities in Japan
Query OK, 1 row affected (0.00 sec)
mysql> SELECT @cities;
+---------+
| @cities |
+---------+
| 248 |
+---------+
1 row in set (0.00 sec)
mysql> CALL citycount('FRA', @cities); -- cities in France
Query OK, 1 row affected (0.00 sec)
mysql> SELECT @cities;
+---------+
| @cities |
+---------+
| 40 |
+---------+
1 row in set (0.00 sec)
该示例使用mysql客户端
delimiter命令在定义过程时将语句定界符从 更改;为//。这使得
;过程主体中使用的定界符能够传递到服务器,而不是被
mysql本身解释。请参阅
第 23.1 节,“定义存储程序”。
RETURNS只能为 a 指定
该子句FUNCTION,这是强制性的。它表示函数的返回类型,函数体必须包含一个语句。如果该
语句返回不同类型的值,则该值被强制转换为正确的类型。例如,如果一个函数在子句中指定一个
或
值
,但该
语句返回一个整数,则该函数返回的值是成员集合中相应成员的字符串。
RETURN
valueRETURNENUMSETRETURNSRETURNENUMSET
以下示例函数采用一个参数,使用 SQL 函数执行操作,然后返回结果。在这种情况下,不需要使用delimiter
,因为函数定义不包含内部
;语句定界符:
mysql> CREATE FUNCTION hello (s CHAR(20))
mysql> RETURNS CHAR(50) DETERMINISTIC
RETURN CONCAT('Hello, ',s,'!');
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world! |
+----------------+
1 row in set (0.00 sec)
参数类型和函数返回类型可以声明为使用任何有效的数据类型。
如果前面有规范
COLLATE,则可以使用该属性。CHARACTER SET
由routine_body有效的 SQL 例程语句组成。这可以是诸如
SELECTor
之类的简单语句,也可以是使用andINSERT编写的复合语句。复合语句可以包含声明、循环和其他控制结构语句。这些语句的语法在第 13.6 节,“复合语句”中描述。实际上,存储函数倾向于使用复合语句,除非主体由单个
语句组成。
BEGINENDRETURN
MySQL 允许例程包含 DDL 语句,例如
CREATEand DROP。MySQL 还允许存储过程(但不是存储函数)包含 SQL 事务语句,例如
COMMIT. 存储函数可能不包含执行显式或隐式提交或回滚的语句。SQL 标准不要求支持这些语句,它声明每个 DBMS 供应商都可以决定是否允许它们。
返回结果集的语句可以在存储过程中使用,但不能在存储函数中使用。此禁令包括SELECT没有子句的语句和其他语句,例如、
和
。对于可以在函数定义时确定返回结果集的语句,会发生错误()。对于只能在运行时确定返回结果集的语句,会出现
错误()。
INTO
var_listSHOWEXPLAINCHECK TABLENot allowed to return a result set from a
functionER_SP_NO_RETSETPROCEDURE %s can't return a result set in the given
contextER_SP_BADSELECT
USE存储例程中的语句是不允许的。调用例程时,将
执行隐式(并在例程终止时撤消)。这导致例程在执行时具有给定的默认数据库。对数据库中除常规默认数据库之外的对象的引用应使用适当的数据库名称进行限定。
USE db_name
有关存储例程中不允许的语句的其他信息,请参阅 第 23.8 节,“对存储程序的限制”。
有关从使用具有 MySQL 接口的语言编写的程序中调用存储过程的信息,请参阅 第 13.2.1 节,“CALL 语句”。
MySQLsql_mode在创建或更改例程时存储有效的系统变量设置,并且始终使用此设置执行例程,
而不管例程开始执行时当前服务器 SQL 模式如何。
从调用程序的 SQL 模式切换到例程的 SQL 模式发生在对参数求值并将结果值赋给例程参数之后。如果您在严格 SQL 模式下定义例程但在非严格模式下调用它,则在严格模式下不会将参数分配给例程参数。如果您要求传递给例程的表达式在严格 SQL 模式下赋值,您应该以有效的严格模式调用例程。
该COMMENT特性是一个MySQL扩展,可用于描述存储例程。此信息由SHOW CREATE
PROCEDUREandSHOW CREATE
FUNCTION语句显示。
该LANGUAGE特性指示编写例程所用的语言。服务器忽略这个特性;仅支持 SQL 例程。
如果例程总是对相同的输入参数产生相同的结果,则该
例程被认为是“确定性的” ,否则被认为是“非确定性的”。如果
例程定义中既未给出 nor ,则默认DETERMINISTIC为. 要声明一个函数是确定性的,您必须
明确指定。
NOT
DETERMINISTICNOT DETERMINISTICDETERMINISTIC
例程性质的评估基于创建者的
“诚实”:MySQL 不检查声明的例程DETERMINISTIC是否没有产生不确定结果的语句。但是,错误声明例程可能会影响结果或影响性能。声明一个不确定的例程
DETERMINISTIC可能会导致优化器做出不正确的执行计划选择,从而导致意外的结果。声明确定性例程 as
NONDETERMINISTIC可能会导致不使用可用的优化,从而降低性能。
如果启用二进制日志记录,该DETERMINISTIC
特性会影响 MySQL 接受哪些例程定义。请参阅第 23.7 节,“存储程序二进制日志记录”。
包含NOW()
函数(或其同义词)或
RAND()不确定的例程,但它可能仍然是复制安全的。对于
NOW(),二进制日志包含时间戳并正确复制。
RAND()只要在例程执行期间仅调用一次,它也能正确复制。(您可以将例程执行时间戳和随机数种子视为源和副本上相同的隐式输入。)
几个特征提供了有关例程使用的数据的性质的信息。在 MySQL 中,这些特性仅供参考。服务器不使用它们来限制允许例程执行的语句类型。
SQL SECURITY特性可以是
或DEFINER指定INVOKER安全上下文;也就是说,例程是使用例程
DEFINER子句中指定的帐户的权限还是调用它的用户的权限执行。此帐户必须有权访问与例程关联的数据库。默认值为
DEFINER。调用例程的用户必须拥有EXECUTE它的特权,DEFINER如果例程在定义者安全上下文中执行,则帐户也必须具有特权。
该DEFINER子句指定在例程执行时检查具有该SQL SECURITY
DEFINER特征的例程的访问权限时要使用的 MySQL 帐户。
如果DEFINER存在该子句,则该
user值应该是指定为 、 或 的
MySQL
帐户
。允许的
值取决于您拥有的权限,如
第 23.6 节“存储对象访问控制”中所述。另请参阅该部分以获取有关存储例程安全性的其他信息。
'user_name'@'host_name'CURRENT_USERCURRENT_USER()user
如果DEFINER省略子句,则默认定义者是执行CREATE
PROCEDUREorCREATE
FUNCTION语句的用户。这与明确指定相同
DEFINER = CURRENT_USER。
SQL SECURITY DEFINER在用特性
定义的存储例程的主体内
,CURRENT_USER函数返回例程的DEFINER值。有关存储例程中的用户审计的信息,请参阅
第 6.2.18 节,“基于 SQL 的帐户活动审计”。
考虑以下过程,它显示
mysql.user系统表中列出的 MySQL 帐户数:
CREATE DEFINER = 'admin'@'localhost' PROCEDURE account_count()
BEGIN
SELECT 'Number of accounts:', COUNT(*) FROM mysql.user;
END;
该过程被分配一个DEFINER帐户,
'admin'@'localhost'无论哪个用户定义它。无论哪个用户调用它,它都以该帐户的权限执行(因为默认安全特性是DEFINER)。该过程成功或失败取决于调用者是否拥有
EXECUTE它
'admin'@'localhost'的
SELECT权限和表的权限
mysql.user。
现在假设该过程是用以下SQL
SECURITY INVOKER特征定义的:
CREATE DEFINER = 'admin'@'localhost' PROCEDURE account_count()
SQL SECURITY INVOKER
BEGIN
SELECT 'Number of accounts:', COUNT(*) FROM mysql.user;
END;
该过程仍然有一个DEFINERof
'admin'@'localhost',但在这种情况下,它以调用用户的权限执行。因此,过程的成功或失败取决于调用者是否拥有EXECUTE它的SELECT特权和表的特权
mysql.user。
服务器按如下方式处理例程参数的数据类型、使用创建的局部例程变量
DECLARE或函数返回值:
检查分配是否存在数据类型不匹配和溢出。转换和溢出问题会导致警告或严格 SQL 模式下的错误。
只能分配标量值。例如,这样的语句
SET x = (SELECT 1, 2)是无效的。对于字符数据类型,如果
CHARACTER SET包含在声明中,则使用指定的字符集及其默认排序规则。如果该COLLATE属性也存在,则使用该排序规则而不是默认排序规则。如果
CHARACTER SET和COLLATE不存在,则使用在例程创建时有效的数据库字符集和排序规则。为避免让服务器使用数据库字符集和排序规则,请为字符数据参数提供显式CHARACTER SET和COLLATE属性。如果更改数据库默认字符集或排序规则,则必须删除并重新创建要使用新数据库默认值的存储例程。
数据库字符集和排序规则由
character_set_database和collation_database系统变量的值给出。有关详细信息,请参阅 第 10.3.3 节,“数据库字符集和排序规则”。