在 MySQL 中,参数是可以被调用程序传入或传出函数(函数只能传入)和存储过程的变量。参数被函数或过程创建时定义在 CREATE 语句内,就像下面所展示的一样:
Create procedure|function( [[IN | OUT |INOUT] parameter_name data_type...] )
语法中:
parameter_name 表示参数名,参数的命名和变量的命名具有相同的规则。
data_type 表示参数类型,可以是任何本地变量类型。
IN 表示输入型参数,除非特别声明,参数默认都是 IN 类型的。这意味这它的值必须被调用程序所指定,并且任何在存储程序内部对该参数的修改都不能在调用程序中起作用。
OUT 表示输出型参数,一个 OUT 类型的参数可以被存储程序所修改,并且这个被修改的值可以在调用程序中生效,调用程序必须提供一个变量来接受由 OUT 参数输出的内容,但是存储程序本身并没有对这个可能已经初始化的变量的操作权限。当存储程序开始执行时,任何 OUT 变量的值都被赋值为 NULL,不管这个值在调用程序中是否被赋予其他值。
INOUT 表示输入输出类型参数,INOUT 参数同时扮演着 IN 和 OUT 参数的角色。那意味着,调用程序可以提供一个值,而存储程序自身可以修改这个参数的值,并且当存储程序结束时调用程序对该修改后的值具有访问权限。
注意:IN,OUT 和 INOUT 关键字只能被应用于存储过程而不适用于存储函数,在存储函数中所有的参数都被视为 IN 参数。
我们将验证 IN 参数的行为。虽然 MySQL 允许我们修改 IN 参数,但这种修改在调用程序中并不可见。例如:
-- 修改命令分隔符 mysql> delimiter # -- 开始创建名为 demo_in_parameter 的存储过程,仅接收一个 INT 类型的输入参数 p_in mysql> CREATE PROCEDURE demo_in_parameter(IN p_in INT) BEGIN -- 输出参数 p_in 的值 SELECT p_in; -- 修改参数的值 SET p_in=2; -- 再次输出参数的值 SELECT p_in; END -> # Query OK, 0 rows affected (0.01 sec) -- 将命令分隔符修改回默认值 mysql> delimiter ; -- 定义一个变量,值为 100 mysql> set @myIn = 100; Query OK, 0 rows affected (0.00 sec) -- 使用定义的变量调用存储过程 mysql> call demo_in_parameter(@myIn); +------+ | p_in | +------+ | 100 | +------+ 1 row in set (0.06 sec) +------+ | p_in | +------+ | 2 | +------+ 1 row in set (0.15 sec) Query OK, 0 rows affected (0.00 sec) -- 再次查看定义的变量,它的值并没有被修改,还是 100 mysql> select @myIn; +-------+ | @myIn | +-------+ | 100 | +-------+ 1 row in set (0.06 sec)
我们将验证 OUT 参数的行为。虽然调用程序已经初始化了 OUT 参数的值,但是被调用程序无法看到这个值。无论如何,调用程序只有在被调用过程执行完成后才能看到该参数的改变。例如:
-- 修改命令分隔符 mysql> delimiter # -- 创建一个名为 demo_out_parameter 的存储过程,仅仅接收一个 OUT 类型的 INT 参数 mysql> CREATE PROCEDURE demo_out_parameter(OUT p_out INT) BEGIN SELECT p_out; SET p_out=2; SELECT p_out; END -> # Query OK, 0 rows affected (0.01 sec) -- 恢复默认命令分隔符 mysql> delimiter ; -- 定义一个变量,初始值为 100 mysql> set @myOut=100; Query OK, 0 rows affected (0.00 sec) -- 调用存储过程 -- 你会发现存储过程不能获取 @myOut 的值,因为它是 OUT 类型的 mysql> call demo_out_parameter(@myOut); +-------+ | p_out | +-------+ | NULL | +-------+ 1 row in set (0.03 sec) +-------+ | p_out | +-------+ | 2 | +-------+ 1 row in set (0.06 sec) Query OK, 0 rows affected (0.00 sec) -- 存储过程执行完成,查看 @myOut 变量的值,它的值被修改为 2 mysql> select @myOut; +--------+ | @myOut | +--------+ | 2 | +--------+ 1 row in set (0.03 sec)
我们演示 INOUT 参数的值,可以为我们的被调程序所见,所修改并返回给它的调用程序。例如:
-- 修改命令分隔符 mysql> delimiter # -- 创建一个名为 demo_inout_parameter 的存储过程,仅仅接收一个 INOUT 的参数 mysql> CREATE PROCEDURE demo_inout_parameter(INOUT p_inout INT) BEGIN SELECT p_inout; SET p_inout=2; SELECT p_inout; END -> # Query OK, 0 rows affected (0.00 sec) -- 恢复默认命令分隔符 mysql> delimiter ; -- 定义一个变量,初始值为 100 mysql> set @myINOUT=100; Query OK, 0 rows affected (0.00 sec) -- 调用存储过程 -- 你会发现存储过程能够看见 @myINOUT 参数的值,并修改后对外也可见 mysql> call demo_inout_parameter(@myINOUT); +---------+ | p_inout | +---------+ | 100 | +---------+ 1 row in set (0.03 sec) +---------+ | p_inout | +---------+ | 2 | +---------+ 1 row in set (0.07 sec) Query OK, 0 rows affected (0.00 sec) -- 存储过程执行完成,查看 @myINOUT 变量的值,它的值被修改为 2 mysql> select @myINOUT; +----------+ | @myINOUT | +----------+ | 2 | +----------+ 1 row in set (0.04 sec)
注意:如果你在 MySQL 的图形界面工具中执行存储过程创建语句,则不需要使用 delimiter 去修改命令分隔符,只有在 MySQL 命令行执行创建存储过程的语句才需要。这是因为存储过程中一般不止一条语句,多条语句使用 “;” 进行分割,与默认的命令行分割符冲突导致。例如:
CREATE PROCEDURE demo_inout_parameter(INOUT p_inout INT) BEGIN SELECT p_inout; SET p_inout=2; SELECT p_inout; END;
MySQL 命令行执行上面语句时,错误如下:
mysql> CREATE PROCEDURE demo_inout_parameter(INOUT p_inout INT) -> BEGIN -> SELECT p_inout; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 3 mysql> SET p_inout=2; ERROR 1193 (HY000): Unknown system variable 'p_inout' mysql> SELECT p_inout; ERROR 1054 (42S22): Unknown column 'p_inout' in 'field list' mysql> END; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END' at line 1
实际上相当于我们执行了一个不完整的存储过程语句,如下:
mysql> CREATE PROCEDURE demo_inout_parameter(INOUT p_inout INT) -> BEGIN -> SELECT p_inout;
这肯定是一个错误的存储过程语句,所以报错,明白了吧!!
如果使用 Navicate Premium 类似工具的命令行则没有则个问题,如下图:
这就是为什么开发人员偏爱可视化工具的原因,不要太方便。