定义
- 存储在数据库端的一组 SQL 语句集。
- 用户可以通过存储过程名和传参多次调用的程序模块。
- 存储过程的特点:
- 使用灵活,可以使用流控语句、自定义变量等完成复杂的业务逻辑。
- 提高数据安全性,屏蔽应用程序直接对表的操作,易于进行审计。
- 减少网络传输。
- 提高代码维护的复杂度,实际使用需要结合业务评估。
语法
CREATE
[DEFINER = { user | CURRENT_USER }]
PROCEDURE sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body
proc_parameter:
[ IN | OUT | INOUT ] param_name 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
-- 删除
DROP PROCEDURE procedure_name;
:::warning 注意:只有 PROCEDURE 才有 IN(传入)、OUT(传出)、INOUT(传入传出)参数,自定义函数(只有)默认就是 IN。 :::
流程控制语句
官方文档:https://dev.mysql.com/doc/refman/5.7/en/flow-control-statements.html
--
-- IF
--
-- 语法
IF search_condition THEN statement_list
[ELSEIF search_condition THEN statement_list] ...
[ELSE statement_list]
END IF
-- 例子
mysql> delimiter //
mysql> create procedure pcd_test_1 (in param_a int) -- 创建一个
-> begin
-> declare a int; -- delcare声明了该变量的作用域在该procedure中
-> if param_a > 10 then set a:=11;
-> elseif param_a = 10 then set a:=10;
-> else set a:=9;
-> end if;
-> end;//
Query OK, 0 rows affected (0.01 sec)
mysql> select @a; -- 查看当前会话中变量a的值
+------+
| @a |
+------+
| NULL | -- 当前会话中a为NULL
+------+
1 row in set (0.00 sec)
mysql> call pcd_test_1(1);
+------+
| a |
+------+
| 9 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call pcd_test_1(10);
+------+
| a |
+------+
| 10 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call pcd_test_1(20);
+------+
| a |
+------+
| 11 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> select @a;
+------+
| @a |
+------+
| NULL | -- 使用了declare,使得procedure中a的作用域限制在了procedure内
+------+
1 row in set (0.00 sec)
--
-- CASE WHEN
--
-- CASE WHEN 语法
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list] ...
[ELSE statement_list]
END CASE
-- 或者是
CASE
WHEN search_condition THEN statement_list
[WHEN search_condition THEN statement_list] ...
[ELSE statement_list]
END CASE
--
-- CASE WHEN 例子
--
mysql> delimiter //
mysql>
mysql> create procedure pcd_test_2(in param_1 int)
-> begin
-> case param_1
-- 当case后面有value时,该value会和when中的when_value进行"="判断
-- 相等则执行then后面的语句,然后跳出;否则就进行下一次when的匹配
-> when 2 then select 200;
-> when 3 then select 300;
-> else
-> begin
-- 当没有匹配时,且else中没有要执行的语句
-- 则给一个begin/end的空语句;
-- 或者不写else语句;
-> end;
-> end case;
-> end;//
Query OK, 0 rows affected (0.03 sec)
mysql> delimiter ;
mysql> call pcd_test_2(1);
Query OK, 0 rows affected (0.00 sec)
mysql> call pcd_test_2(2);
+-----+
| 200 |
+-----+
| 200 |
+-----+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call pcd_test_2(3);
+-----+
| 300 |
+-----+
| 300 |
+-----+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
-- 另外一种SQL语法请参考rank排名作业;注意when后跟的是condition
--
-- WHILE 循环
--
-- WHILE 语法
[begin_label:] WHILE search_condition DO
statement_list
END WHILE [end_label]
-- WHILE举例
mysql> delimiter //
mysql>
mysql> create procedure pcd_test_3(in param_1 int)
-> begin
-> declare a int default 1;
-> while param_1 > 10 do
-> set param_1 = param_1 - 1;
-> set a = a + 1;
-> end while;
-> select a;
-> end;//
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql> call pcd_test_3(15); -- 15 - 10 = 5;需要5次循环
+------+
| a |
+------+
| 6 | -- a + 5 = 6
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
--
-- REPEAT 循环
--
-- REPEAT 语法
[begin_label:] REPEAT
statement_list
UNTIL search_condition
END REPEAT [end_label]
mysql> delimiter //
mysql> create procedure pcd_test_4(in param_1 int)
-> begin
-> SET @x = 0; -- 没有使用declare,所以x是会话级别的
-> REPEAT
-> SET @x = @x + 1;
-> UNTIL @x > param_1 END REPEAT;
-> end;//
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql> call pcd_test_4(10);
Query OK, 0 rows affected (0.00 sec)
mysql> select @x; -- x是会话级别的
+------+
| @x |
+------+
| 11 | -- 一共循环11次(10>10 为False,11 > 10为True,才跳出)
+------+
1 row in set (0.00 sec)
--
-- loop 循环
--
-- loop语法
[begin_label:] LOOP
statement_list
END LOOP [end_label]
-- ITERATE 和label相结合,表示继续从label处执行
-- LEAVE 和label相结合,表示从label 标记的代码段离开
-- loop 例子
mysql> delimiter //
mysql> create procedure pcd_test_5(in param_1 int)
-> begin
-> test_label: loop
-> set param_1 := param_1 + 1; -- 参数累加
-> if param_1 < 10 then -- 如果累加的值小于10
-> iterate test_label; -- 继续执行 标签 test_label
-> end if;
-> leave test_label; -- 如果>=10则离开这个test_label(loop)
-> end loop test_label;
-> set @x = param_1; -- 设置会话级别的变量
-> end;//
Query OK, 0 rows affected (0.02 sec)
mysql> delimiter ;
mysql> call pcd_test_5(5); -- 5<10 ,累加5次后>=10为true,离开循环
Query OK, 0 rows affected (0.00 sec)
mysql> select @x;
+------+
| @x |
+------+
| 10 | -- 累加到10的 param_1 赋值给 x, 即为10
+------+
1 row in set (0.00 sec)
-- 老师给出的例子, 阶乘
mysql> create table test_proc_1(a int, b int); -- 给一个存放数据的表
Query OK, 0 rows affected (0.15 sec)
mysql> delimiter //
mysql> create procedure proc_test1(in total int, out res int)
-> begin
-> declare i int;
-> set i := 1;
-> set res := 1;
-> if total <= 0 then
-> set total := 1;
-> end if;
-> while i <= total do
-> set res := res * i;
-> insert into test_proc_1 values(i, res);
-> set i := i + 1;
-> end while;
-> end;//
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql> set @res_value := 0;
Query OK, 0 rows affected (0.00 sec)
mysql> call proc_test1(5, @res_value); -- 因为res是out变量,要预先有这个变量,这里上面设置了res_value(实参和形参不必同名)
Query OK, 1 row affected (0.15 sec)
mysql> select @res_value;
+------------+
| @res_value |
+------------+
| 120 | -- 5的阶乘的结果是120
+------------+
1 row in set (0.00 sec)
mysql> select * from test_proc_1;
+------+------+
| a | b |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 6 |
| 4 | 24 |
| 5 | 120 | -- 每次insert的结果
+------+------+
5 rows in set (0.00 sec)
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/wgmill 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。