Documentation Home
MySQL 8.0 参考手册  / 第 13 章 SQL 语句  / 13.1 数据定义语句  /  13.1.15 CREATE PROCEDURE 和 CREATE FUNCTION 语句

13.1.15 CREATE PROCEDURE 和 CREATE FUNCTION 语句

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 PROCEDURECREATE FUNCTION需要 CREATE ROUTINE特权。如果 DEFINER存在该子句,所需的权限取决于user值,如第 20.6 节“存储对象访问控制”中所述。如果启用二进制日志记录,CREATE FUNCTION 可能需要SUPER权限,如第 20.7 节“存储程序二进制日志记录”中所述。

默认情况下,MySQL 自动授予 例程创建者权限ALTER ROUTINE和 权限。EXECUTE可以通过禁用 automatic_sp_privileges系统变量来更改此行为。请参阅第 20.2.2 节,“存储例程和 MySQL 权限”

DEFINERSQL SECURITY 子句指定在例程执行时检查访问权限时要使用的安全上下文,如本节后面所述 。

如果例程名称与内置 SQL 函数的名称相同,则会出现语法错误,除非您在定义例程或稍后调用例程时在名称和后面的括号之间使用空格。因此,请避免将现有 SQL 函数的名称用于您自己的存储例程。

SQL 模式适用于IGNORE_SPACE内置函数,不适用于存储例程。无论是否 IGNORE_SPACE启用,存储例程名称后始终允许有空格。

括号内的参数列表必须始终存在。如果没有参数, ()则应使用空参数列表。参数名称不区分大小写。

默认情况下,每个参数都是一个IN参数。要为参数指定其他方式,请 在参数名称之前 使用关键字OUT或。INOUT

笔记

将参数指定为INOUTINOUT仅对PROCEDURE. 对于 a FUNCTION,参数始终被视为 IN参数。

参数将IN值传递给过程。过程可能会修改该值,但当过程返回时,调用者看不到修改。参数将过程中的OUT值传递回调用者。它的初始值 NULL在过程中,当过程返回时,它的值对调用者可见。参数由 INOUT调用者初始化,可以由过程修改,过程返回时调用者可以看到过程所做的任何更改。

对于每个OUTINOUT 参数,在 CALL调用该过程的语句中传递一个用户定义的变量,以便您可以在该过程返回时获取它的值。如果您从另一个存储过程或函数中调用该过程,您还可以将例程参数或局部例程变量作为OUT orINOUT参数传递。如果您从触发器中调用该过程,您还可以 作为 or参数传递。 NEW.col_nameOUTINOUT

有关未处理条件对过程参数的影响的信息,请参阅 第 13.6.7.8 节,“条件处理和 OUT 或 INOUT 参数”

在例程内准备的语句中不能引用例程参数;参见 第 20.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本身解释。请参阅 第 20.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

有关存储例程中不允许的语句的其他信息,请参阅 第 20.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 接受哪些例程定义。请参阅第 20.7 节,“存储程序二进制日志记录”

包含NOW() 函数(或其同义词)或 RAND()不确定的例程,但它可能仍然是复制安全的。对于 NOW(),二进制日志包含时间戳并正确复制。 RAND()只要在例程执行期间仅调用一次,它也能正确复制。(您可以将例程执行时间戳和随机数种子视为源和副本上相同的隐式输入。)

几个特征提供了有关例程使用的数据的性质的信息。在 MySQL 中,这些特性仅供参考。服务器不使用它们来限制允许例程执行的语句类型。

  • CONTAINS SQL表示例程不包含读取或写入数据的语句。如果没有明确给出这些特征,则这是默认值。此类语句的示例是SET @x = 1or DO RELEASE_LOCK('abc'),它们执行但既不读取也不写入数据。

  • NO SQL表示例程不包含 SQL 语句。

  • READS SQL DATA指示例程包含读取数据的语句(例如, SELECT),但不包含写入数据的语句。

  • MODIFIES SQL DATA指示例程包含可能写入数据的语句(例如, INSERTDELETE)。

SQL SECURITY特性可以是 或DEFINER指定INVOKER安全上下文;也就是说,例程是使用例程 DEFINER子句中指定的帐户的权限还是调用它的用户的权限执行。此帐户必须有权访问与例程关联的数据库。默认值为 DEFINER。调用例程的用户必须拥有EXECUTE它的特权,DEFINER如果例程在定义者安全上下文中执行,则帐户也必须具有特权。

DEFINER子句指定在例程执行时检查具有该SQL SECURITY DEFINER特征的例程的访问权限时要使用的 MySQL 帐户。

如果DEFINER存在该子句,则该 user值应该是指定为 、 或 的 MySQL 帐户 。允许的 值取决于您拥有的权限,如 第 20.6 节“存储对象访问控制”中所述。另请参阅该部分以获取有关存储例程安全性的其他信息。 'user_name'@'host_name'CURRENT_USERCURRENT_USER()user

如果DEFINER省略子句,则默认定义者是执行CREATE PROCEDUREorCREATE FUNCTION语句的用户。这与明确指定相同 DEFINER = CURRENT_USER

SQL SECURITY DEFINER在用特性 定义的存储例程的主体内 ,CURRENT_USER函数返回例程的DEFINER值。有关存储例程中的用户审计的信息,请参阅 第 6.2.15 节,“基于 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 SETCOLLATE不存在,则使用在例程创建时有效的数据库字符集和排序规则。为避免让服务器使用数据库字符集和排序规则,请为字符数据参数提供显式 CHARACTER SETCOLLATE属性。

    如果更改数据库默认字符集或排序规则,则必须删除并重新创建要使用新数据库默认值的存储例程。

    数据库字符集和排序规则由 character_set_databasecollation_database系统变量的值给出。有关详细信息,请参阅 第 10.3.3 节,“数据库字符集和排序规则”