存储过程是一组为了完成某项特定功能的SQL语句集,它可以由声明式SQL语句和过程式SQL语句组成,这组语句经过编译后会存储在数据库中,用户只需要通过指定存储过程的名字并给定参数,即可随时调用并执行它,而不必重新编译。
使用存储过程的好处如下:
① 可增强SQL语言的功能和灵活性;
② 良好的封装性;
③ 高性能;
④ 可减少网络流量;
⑤ 存储过程可作为一种安全机制来确保数据库的安全性和数据的完整性。
在MYSQL中服务器处理SQL默认是以分号作为语句的结束标志,然而在创建存储过程中,存储过程体中可能包含多条SQL语句,这些SQL语句如果仍以分号作为语句结束符,那么服务器在处理时会以遇到的第一条SQL语句结尾处的分号作为语句结束符,而不再去处理存储过程体中后面的SQL语句,为了解决这个问题,需要使用DELIMITER命令,这MYSQL语句的结束标志临时修改为其他符号。例如:DELIMITER !!
创建存储过程的语法格式如下:
/*创建存储过程的语法格式*/
CREATE PROCEDURE pro_name([param[,...]]) routine_body
/*其中param的语法格式如下*/
[IN|OUT|INOUT] param_name type
简单示例如下:
/*创建存储过程将emp表中员工编号为8888的员工工资更改为3500*/
delimiter **
create procedure pro_emp(in emp_no int,in emp_sal double)
begin
update emp set sal = emp_sal where empno = emp_no;
end **
/*调用存储过程*/
call pro_emp(8888,3500);
局部变量用来存储存储过程体中的临时结果,在MYSQL中使用DECLARE语句声明局部变量,并且可以赋予其一个初始的值,如果没有指定则默认为NULL。声明语法如下:
DECLARE var_name[,...] type [DEFAULT value]
示例如下:
declare var_id int;
注意:
① 局部变量只能在存储过程体的BEGIN END语句块中声明,作用范围也仅限于BEGIN END语句块;
② 局部变量必须在存储过程的开头处声明;
③ 局部变量不同于用户变量,用户变量在声明时,会在其名称前面加一个@符号,同时已经声明的用户变量存在于整个会话之中。
使用SET语句可以为局部变量赋值,语法格式如下:
SET var_name = expr[,var_name = expr] ...
示例如下:
set var_id = 1001;
该语句把选定列的值直接存储到局部变量中,其语法格式如下:
SELECT column_name[,...] INTO var_name[,...] table_expr
注意:该语句返回的结果集只能有一行数据
流程控制语句包括条件判断语句(如:IF THEN ELSE语句和CASE WHEN语句)和循环语句(如:WHILE语句、REPEAT语句和LOOP语句),循环语句中还可以使用ITERATE语句,用于表示退出当前循环,且重新开始一个循环。
游标是一个被SELECT语句检索出来的结果集,在存储了游标后,用户就可以根据需要滚动或浏览其中的数据,使用游标的步骤如下:
① 声明游标
使用游标之前必须先声明它,这个过程并没有检索数据,只是定义要使用的SELECT语句,创建游标的语法格式如下:
CREATE cusor_name CURSOR FOR select_sql
注意:此处的SELECT语句中不能有INTO子句
② 打开游标
定义游标之后,必须打开才能使用,这个过程实际上是将游标连接到由SELECT语句返回的结果集中,其语法格式如下:
OPEN cursor_name
注意:在实际应用中,一个游标可以被多次打开,由于其他用户或应用程序可能随时更新了数据表,因此每次打开游标的结果集可能会有所不同。
③ 读取数据
使用FETCH INTO语句可以从游标中读取数据,其语法格式如下:
FETCH cursor_name INTO var_name [,var_name] ...
④ 关闭游标
使用语法如下:
CLOSE cursor_name
每个游标不再使用时都应该被关闭,使用CLOSE语句将会释放游标所使用的全部资源,游标被关闭后如果没有重新打开,则不能被使用。如果没有明确关闭游标,MYSQL将会在到达END语句时自动关闭它。 下面是一个综合示例:
/*存储过程综合使用示例:计算emp表的行数*/
delimiter !!
create procedure pro_emp_rows(out rows int)
begin
declare emp_no int;
declare b boolean default true;
declare cur_no cursor for select empno from emp;
declare continue handler for not found set b = false;
set rows = 0;
open cur_no;
fetch cur_no into emp_no;
while b do set rows = rows + 1;
fetch cur_no into emp_no;
end while;
close cur_no;
end!!
调用存储过程并获取结果如下:
call pro_emp_rows(@rows);
select @rows;
上例中,定义了一个CONTINUE HANDLER句柄,它是在条件出现时被执行的代码,用于控制循环语句,以实现游标的下移;DECLARE语句的使用存在特定的次序,即用DECLARE语句定义的局部变量必须在定义任意游标或句柄之前定义,而句柄必须在游标之后定义,否则系统会出现错误消息。
注意:
① 游标只能用于存储过程或存储函数中,不能单独在查询操作中使用;
② 在存储过程或存储函数中,可以定义多个游标,但是在一个BEGIN END每一个游标的名字必须是唯一的;
③ 游标不是一条SELECT语句,是被SELECT语句检索出来的结果集。
示例如下:
/*删除存储过程*/
drop procedure if exists pro_emp_rows
存储过程与存储函数的区别如下:
① 存储函数不能拥有输出参数,这是因为存储函数自身就是输出参数;而存储过程可以拥有输出参数;
② 可以不使用CALL语句,直接调用存储函数;而对存储过程的调用,需要使用存储函数;
③ 存储函数中必须有一个RETURN语句;而存储过程中不能包含RETURN语句。
创建存储函数的语法格式如下:
/*创建存储函数的语法格式*/
CREATE FUNCTION fun_name([fun_param[,...]])
RETURNS type
routine_body
注意:
① 存储函数不能跟存储过程同名;
② 存储函数的参数只有参数名和类型,不能指定关键字IN OUT INOUT;
③ RETURNS子句用于声明存储函数返回的数据类型,type用于指定返回值的数据类型。
示例如下:
/*创建一个存储函数示例:根据给定的empno查询员工的姓名*/
delimiter !!
create function fun_emp_name(emp_no int)
returns varchar(20) deterministic
begin
return (select ename from emp where empno = emp_no);
end!!
/*调用存储函数*/
select fun_emp_name(8888) from dual;
示例如下:
drop function if exists fun_emp_name