存储函数
和存储过程
都属于存储例程
,都是对某些语句的一个封装。存储函数
侧重于执行这些语句并返回一个值,而存储过程
更侧重于单纯的去执行这些语句。先看一下存储过程
的定义语句:mysql
CREATE PROCEDURE 存储过程名称([参数列表])
BEGIN
须要执行的语句
END
复制代码
与存储函数
最直观的不一样点就是,存储过程
的定义不须要声明返回值类型
。为了更直观的理解,咱们先定义一个存储过程
看看:程序员
mysql> delimiter $
mysql> CREATE PROCEDURE t1_operation(
-> m1_value INT,
-> n1_value CHAR(1)
-> )
-> BEGIN
-> SELECT * FROM t1;
-> INSERT INTO t1(m1, n1) VALUES(m1_value, n1_value);
-> SELECT * FROM t1;
-> END $
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql>
复制代码
咱们创建了一个名叫t1_operation
的存储过程,它接收两个参数,一个是INT
类型的,一个是CHAR(1)
类型的。这个存储过程作了3件事儿,一件是查询一下t1
表中的数据,第二件是根据接收的参数来向t1
表中插入一条语句,第三件是再次查询一下t1
表中的数据。sql
存储函数
执行语句并返回一个值,因此经常使用在表达式中。存储过程
偏向于调用那些语句,并不能用在表达式中,咱们须要显式的使用CALL
语句来调用一个存储过程
:数据库
CALL 存储过程([参数列表]);
复制代码
比方说咱们调用一下t1_operation
存储过程能够这么写:bash
mysql> CALL t1_operation(4, 'd');
+------+------+
| m1 | n1 |
+------+------+
| 1 | a |
| 2 | b |
| 3 | c |
+------+------+
3 rows in set (0.00 sec)
+------+------+
| m1 | n1 |
+------+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
+------+------+
4 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql>
复制代码
从执行结果中能够看到,存储过程在执行中产生的全部结果集,所有将会被显示到客户端。函数
小贴士:
只有查询语句才会产生结果集,更新语句是不产生结果集的,因此那条`INSERT`语句所产生的输出没有被显示出来。
复制代码
与存储函数
相似,存储过程
也有类似的查看和删除语句,咱们下边只列举一下相关语句,就不举例子了。学习
查看当前数据库中建立的存储过程
都有哪些的语句:优化
SHOW PROCEDURE STATUS [LIKE 须要匹配的函数名]
复制代码
查看某个存储过程
定义的语句:ui
SHOW CREATE PROCEDURE 存储过程名称
复制代码
删除存储过程
的语句:spa
DROP PROCEDURE 存储过程名称
复制代码
上边在唠叨存储函数
中使用到的各类语句,包括变量的使用、判断、循环结构、注释的使用均可以被用在存储过程
中,这里就再也不赘述了。
比存储函数
牛逼的一点是,存储过程
在定义参数的时候能够选择参数类型(注意!不是数据类型),就像是这个样子:
参数类型 参数名 数据类型
复制代码
这个所谓的参数类型
有下边3种:
参数类型 | 实际参数是否必须是变量 | 描述 |
---|---|---|
IN |
否 | 用于调用者向过程传递数据,若是该参数在过程当中被修改,调用者不可见 |
OUT |
是 | 用于把过程产生的结果放到此类型的参数中,过程结束后调用者能够经过访问该参数来获取过程执行的结果 |
INOUT |
是 | 综合IN 和OUT 的特色,既能够用于调用者向过程传递数据,也能够用于存放过程当中产生的结果以供调用者使用 |
这么直接描述有些生硬哈,咱们一个一个来举例子仔细分析一下:
IN
参数类型
先定义一个类型参数是IN
的存储过程p_in
:
mysql> delimiter $
复制代码
mysql> CREATE PROCEDURE p_in ( -> IN arg INT -> ) -> BEGIN -> -- 语句一:读取参数 -> SELECT arg; -> -- 语句二:为参数赋值 -> SET arg = 123; -> END $ Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql>
```
这个`p_in`存储过程只有一个参数`arg`,它的参数类型是`IN`,这个存储过程实际执行两个语句,第一个语句是用来读取参数的值,第二个语句是给参数赋值。咱们调用一下`p_in`:
```
mysql> SET @a = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL p_in(@a);
+------+
| arg |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @a;
+------+
| @a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql>
```
咱们在客户端定义了一个变量`a`并赋值`1`,由于它是在客户端定义的,因此须要加`@`前缀,而后把它看成参数传给`p_in`存储过程。从结果中能够看出,第一个读取语句被成功执行,虽然第二个语句没有报错,可是再存储过程执行完毕后,再次查看变量`a`的值并无改变,这也就是说:<span style="color:red">IN参数类型的变量只能用于读取,对类型的变量赋值是不会被调用者看到的</span>。
另外,<span style="color:red">由于对于参数类型是`IN`的参数,咱们只是想在存储函数执行中使用它,并不须要把执行结果存储到它里边,因此除了让变量做为函数参数,常量也是能够的</span>,好比这样:
```
mysql> CALL p_in(1);
+------+
| arg |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql>
```
复制代码
OUT
参数类型
先定义一个类型参数是OUT
的存储过程p_out
:
mysql> delimiter $
复制代码
mysql> CREATE PROCEDURE p_out ( -> OUT a INT -> ) -> BEGIN -> SELECT a; -> SET a = 123; -> END $ Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql>
```
这个`p_out`存储过程只有一个参数`arg`,它的参数类型是`OUT`,`p_out`存储过程也有两个语句,一个用于读取参数的值,另外一个用于为参数赋值,咱们调用一下`p_out`:
```
mysql> SET @b = 2;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL p_out(@b);
+------+
| a |
+------+
| NULL |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @b;
+------+
| @b |
+------+
| 123 |
+------+
1 row in set (0.00 sec)
mysql>
```
咱们在客户端定义了一个变量`b`并赋值`2`,而后把它看成参数传给`p_out`存储过程。从结果中能够看出,第一个读取语句并无获取到参数的值,在存储过程执行完毕以后,再次读取变量`b`的值,发现它的值已经被设置成`123`,说明在过程当中对该变量的赋值对调用者是可见的!这也就是说:<span style="color:red">OUT参数类型的变量只能用于赋值,对类型的变量赋值是会被调用者看到的</span>。
另外,<span style="color:red">因为`OUT`参数类型的参数只是为了用于在过程当中赋值后被调用者查看,那实际的参数就不容许是常量,常量还怎么赋值啊</span>!
复制代码
INOUT
参数类型
知道了IN
和OUT
参数类型的意思,INOUT
也就明白了,这种类型的参数既能够在存储过程当中被读取,也能够被赋值后被调用者看到,因此要求实际的参数必须是一个变量,否则还怎么赋值啊!INOUT
参数类型就不具体举例子了,你本身试试哈~
须要注意的是,若是咱们不写明参数类型的话,该参数的类型默认是IN
,咱们以前一直没有注明参数类型,因此以前使用的参数类型都是IN
!
因为能够传入多个OUT
或者INOUT
类型的参数,因此咱们能够在一个存储过程当中得到多个结果,好比这样:
mysql> delimiter $
mysql> CREATE PROCEDURE get_score_data(
-> OUT max_score DOUBLE,
-> OUT min_score DOUBLE,
-> OUT avg_score DOUBLE,
-> s VARCHAR(100)
-> )
-> BEGIN
-> SELECT MAX(score), MIN(score), AVG(score) FROM student_score WHERE subject = s INTO max_score, min_score, avg_score;
-> END $
Query OK, 0 rows affected (0.02 sec)
mysql> delimiter ;
mysql>
复制代码
咱们定义的这个get_score_data
存储过程接受4个参数,前三个参数都是OUT
类型的参数,第四个参数没写参数类型,默认就是IN
类型。存储过程的内容是将指定学科的最高分、最低分、平均分赋值给三个OUT
类型的参数。在这个存储过程执行完以后,咱们能够根据经过访问这几个OUT
类型的参数来得到相应的最高分、最低分以及平均分:
mysql> CALL get_score_data(@a, @b, @c, '母猪的产后护理');
Query OK, 1 row affected (0.01 sec)
mysql> SELECT @a, @b, @c;
+------+------+------+
| @a | @b | @c |
+------+------+------+
| 100 | 55 | 73 |
+------+------+------+
1 row in set (0.00 sec)
mysql>
复制代码
这个例子说明了:咱们能够在存储过程当中向调用者返回多个值,而存储函数只能返回一个值。
存储过程
和存储函数
都是某些语句的一个封装,并且使用的语法格式都是同样的,下边咱们着重说一下它们的不一样点以加深你们的对这二者区别的印象:
存储函数在定义时须要显式用RETURNS
语句标明返回的数据类型,并且在函数体中必须使用RETURN
语句来显式指定返回的值,存储过程不须要。
存储函数的参数类型只能是IN
,而存储过程支持IN
、OUT
、INOUT
三种参数类型。
存储函数只能返回一个值,而存储过程能够经过设置多个OUT
类型的参数来返回多个结果。
存储函数执行过程当中产生的结果集并不会被显示到客户端,而存储过程执行过程当中产生的结果集会被显示到客户端。
存储函数的调用直接使用在表达式中,而存储过程只能经过CALL
语句来显式调用。
本系列专栏都是MySQL入门知识,想看进阶知识能够到小册中查看:《MySQL是怎样运行的:从根儿上理解MySQL》的连接 。小册的内容主要是从小白的角度出发,用比较通俗的语言讲解关于MySQL进阶的一些核心概念,好比记录、索引、页面、表空间、查询优化、事务和锁等,总共的字数大约是三四十万字,配有上百幅原创插图。主要是想下降普通程序员学习MySQL进阶的难度,让学习曲线更平滑一点~