一、学习任务1:为什么使用存储过程和函数
在MySQL数据库中,数据库对象表是存储和操作数据的逻辑结构,而本章所要介绍的数据库对象存储过程和函数,则用来实现将一组关于表操作的SQL语句代码当作一个整体来执行,也是与数据库对象表关联最紧密的数据库对象。在数据库系统中,当调用存储过程和函数时,则会执行这些对象中所设置的SQL语句组,从而实现相应的功能。
通过前面章节的学习,用户不仅能够编写操作单表的单条SQL语句,而且还能够编写操作多表的SQL单条语句。但是针对表的一个完整操作往往不是单条SQL语句就可以实现,而是需要一组 SQL语句来实现。
例如,为了完成购买商品的订单处理,需要考虑如下的情形。
(1)在生成订单信息之前,首先需要查看商品库存中是否有相应的商品。
(2)如果商品库存中存在相应的商品,接着需要预定商品以便不将该商品卖给别人,并且修改库存物品数量以反映正确的库存量。
(3)如果商品库存中不存在相应的商品,就需要向供应商订货。
对于上述一个完整操作,它显示不是单条SQL语句所能实现。因为实现这个完整操作需要编写针对许多表的多条SQL语句,此外在具体执行过程中,这些SQL语句的执行顺序也不是固定的,它会根据相应条件而变化。
在具体应用中,一个完整的操作会包含多条SQL语句,在执行过程中需要根据前面SQL语句的执行结果有选择的执行后面的SQL语句。为了解决该问题,MySQL软件提供了数据库对象存储过程和函数。
触发器可以简单理解为一条或多条SQL语句的集合。存储过程和函数就是事先经过编译并存储在数据库中的一段SQL语句集合。触发器的执行不是由程序调用,也不是由手动启动,而是由事件来触发、激活从而实现执行。而存储过程和函数的执行,则需要手动调用存储过程和函数的名字并需要指定相应的参数。
存储过程和函数有什么区别呢?这两者的区别主要在于函数必须有返回值,而存储过程则没有(不能使用return关键字)。存储过程的参数类型远远多于函数参数类型。
既然己经知道为什么要使用存储过程和函数,那么使用存储过程和函数有哪些优点和缺点呢? 关于存储过程和函数的优点如下:
• 存储过程和函数允许标准组件式编程,提高了SQL语句的重用性、共享性和可移植性。
• 存储过程和函数能够实现较快的执行速度,能够减少网络流量。
• 存储过程和函数可以被作为一种安全机制来利用。
上述优点可以概述成简单和高性能,不过在具体使用存储过程和函数时,也需要了解这些数据库对象的缺陷,分别为:
• 存储过程和函数的编写比单句SQL语句复杂,需要用户具有更高的技能和更丰富的经验。
• 在编写存储过程和函数时,需要创建这些数据库对象的权限。
二、学习任务2:创建存储过程和函数
存储过程和函数的操作包括创建存储过程和函数、查看存储过程和函数、更新存储过程和函数,以及删除存储过程和函数。本节将详细介绍如何创建存储过程和函数。
2.1 创建存储过程语法形式
在MySQL中创建存储过程通过SQL语句CREATE PROCEDURE来实现,其语法形式如下:
CREATE PROCEDURE procedure_name([procedure_parameter[,…]])
[characteristic…] routine_body;
在上述语句中,procedure_name参数表示所要创建的存储过程名字,procedure_parameter参数表示存储过程的参数,characteristic参数表示存储过程的特性,routine_body参数表示存储过程的SQL 语句代码,可以用BEGlN...END来标志SQL语句的开始和结束。
注意:在具体创建存储过程时,存储过程名不能与已经存在的存储过程名重名。除了上述要求外,推荐存储过程名命名(标识符)为procedure_xxx或者proce_xxx;
procedure_parameter中每个参数的语法形式如下:
[IN|OUT|INOUT] parameter_name type
在上述语句中,每个参数由3部分组成,分别为输入输出类型、参数名和参数类型。其中输入/ 输出类型有3种类型,分别为IN:表示输入类型;OUT:表示输出类型;INOUT表示输入/输出类型。parameter_name表示参数名。type表示参数类型,可以是MySQL软件所支持的任意一个数据类型。
characteristic参数的取值为:
LANGUAGE SQL
|[NOT] DETERMINISTIC
|{CONTAINS SQL|NO SQL|READS SQL DATA | MODIFIES SQL DATA}
|SQL SECURITY {DEFINER|INVOKER}
|COMMENT 'string'
当characteristic参数的值为上述各个值时,分别表不:
- LANGUAGE SQL,表示存储过程的routine_body部分由SQL语言的语句组成,为MySQL 软件所有默认的语句。
- [NOT] DETERMINISTIC,表示存储过程的执行结果是否确定。如果值为DETERMINISTIC,表示执行结果是确定的。即每次执行存储过程时,如果输入相同的参数将得到相同的输出; 如果值为NOT DETERMNISTIC,表示执行结果不确定,即相同的输入可能得到不同的输出。默认值为DETERMINISTIC。
- {CONTAINS SQL|NO SQL|READS SQL DATA|MODIFIES SQL DATA},表示使用SQL语句的限制。如果值为CONTAINS SQL表示可以包含SQL语句,但不包含写数据的语句;如果值为NO SQL表示不包含SQL语句;如果值为READS SQL DATA表示包含读数据的语句;如果值为MODIFIES SQL DATA 表示包含写数据的语句。默认值为CONTAINS SQL。
- SQL SECURITY (DEFINER|INVOKER),设置谁有权限来执行。如果值为DEFINER,表示只有定义者自己才能够执行;如果值为INVOKER表示调用者可以执行。默认值为DEFINER。
- COMMENT 'string',表示注释语句。
2.2 创建函数语法形式
在MySQL中创建函数通过SQL语句CREATE FUNCTION来实现,其语法形式如下:
CREATE FUNCTION function _name([function_parameter[,…]]) [characteristic…] routine_body;
在上述语句中,function _name参数表示所要创建的函数名字;function_parameter参数表示函数的参数,characteristic参数表示函数的特性,该参数的取值与存储过程中的取值相同。routine_body 参数表示函数的SQL语句代码,可以用BEGEN...END来标示SQL语句的开始和结束。
注意:在具体创建函数时,函数名不能与已经存在的函数名重名,除了上述要求外,推荐函数名命名(标识符)为function_xxx或者func_xxx。
function_parameter中每个参数的语法形式如下:
parameter_name type
在上述语句中,每个参数由两部分组成,分别为参数名和参数类型。parameter_name表示参数名。type表示参数类型,可以是MySQL软件所支持的任意一个数据类型。
2.3 创建简单的存储过程和函数
前面两节详细介绍了关于存储过程和函数的语法形式,本节将通过具体的实例来讲述如下应用存储过程和函数。
【实例14-1】执行SQL语句CREATE PROCEDURE,在数据库company中,创建查询雇员表 (t_employee)中所有雇员工资的存储过程,具体如下:
执行SQL语句CREATE PROCEDURE,创建名为proce_employee_sal的存储过程,具体SQL语句如下:
DELIMITER $$
CREATE PROCEDURE proce_employee_sal()
COMMENT '查询所有雇员的工资'
BEGIN
SELECT sal FROM t_employee;
END$$
DELIMITER ;
【代码说明】在上述代码中,创建了一个名为proce_enployee_sal的存储过程,主要用来实现通过SELECT语句从t_employee表中查询sal字段值,实现査询雇员工资功能。
【运行效果】执行上面的SQL语句,其结果如图所示。
执行结果没有显示任何错误,表示该存储过程对象proce_employee_sal已经创建成功。
通常在创建存储过程时,经常通过命令“DELIMITER $$”将SQL语句的结束符由“;”符号修改成“$$”。这主要是因为SQL语句中默认语句结束符为分号(;),即存储过程中的routine_body语句也需要用分号来结束,当将结束符号修改成“$$”符号后就可以在执行过程中避免冲突。不过最后一定不要忘记通过命令“DELIMITER ;”将结束符号修改成SQL语句中默认的结束符号。
【实例14-2】执行SQL语句CREATE FUNCTION,在数据库company中,创建査询雇员表(t_empIoyee)中某个雇员工资的函数,具体如下:
执行SQL语句CREATE FUNCTION,创建名为func_employee_sal的函数,具体SQL语句如下:
DELIMITER $$
CREATE FUNCTION func_employee_sal(empno INT(11))
RETURNS DOUBLE(10,2)
COMMENT“查询某个雇员的工资”
BEGIN
RETURN (SELECT sal FROM t_employee WHERE t_employee.empno=empno);
END$$
DELIMITER ;
【代码说明】在上述代码中,创建了一个名为func_employee_sal的函数,该函数拥有一个类型 为int(11)名为empno的参数,返回值为double(10,2)类型。SELECT语句从t_employee表中查询empno字段值等于所传入参数empno值的记录,同时并将该条记录的sal字段的值返回。
【运行效果】执行上面的SQL语句,其结果如图所示。
执行结果没有显示任何错误,表示该函数对象func_employee_sal已经创建成功。在具体创建函数时,与创建存储过程一样,也需要将SQL语句结束符由“;”符号 修改成“$$”,最后通过命令“DELIMITER ;”将结束符号修改成SQL语句中默认的结束符号。
三、学习任务3:关于存储过程和函数表达式
在MySQL软件中,除了支持标准的存储过程和函数外,还对其进行了扩充,引入了表达式。表达式与其他高级语言的表达式一样,由变量、运算符和流程控制来构成。本节将详细介绍存储过程和函数中所包含的表达式语句。
3.1 操作变量
变量是表达式语句中最基本的元素,可以用来临时存储数据。对于MySQL软件来说,可以通过变量存储从表中査询到的数据等。下面将介绍如何声名变量和给变量赋值。
1.声明变量
在MySQL中定义变量通过关键字DECLARE来实现,其语法形式如下:
DECLARE var_name[, …] type [DEFAULT value]
在上述语句中,var_name参数表示所要声明的变量名字;参数TYPE表示所要声明变最的类型; DEFAULT value用来实现设置变量的默认值,如果无该语句默认值为NULL。
注意:在具体声明变量时,可以同时定义多个变量。
2.赋值变置
变量赋值通过关键字SET来实现,其语法形式如下:
SET var_name=expr[,…]
在上述语句中,var_name参数表示所要赋值变量名字;参数expr是关于变量的赋值表达式。
注意:在为变量赋值时,可以同时为多个变量执值,各个变量的赋值语句之间用逗号隔开。
为变量赋值时,除了上述语法外,还可以通过关键字“SELECT...INTO”语句来实现,其语法形式如下:
SELECT field_name[,...] INTO var_name[,…] FROM table_name WHERE condition;
在上述语句中将查询到结果赋值给变量,参数field_name表示查询的字段名,参数var_name 参数表示变量名。
注意:当将查询结果赋值给变量时,该查询语句的返回结果只能是单行。
【实例14-4】下面将通过具体的实例来演示如何声明变量和为变量赋值,具体步骤如下:
(1)执行带有关键字DECLARE的语句,声明一个名为employee_sal的变量,具体SQL语句如下:
DECLARE employee_sal INT DEFAULT 1000;
【代码说明】上述语句声明了一个表示雇员工资的变量employee_sal,并设置该变量的默认值为 1000。
(2)执行带有关键字SET的语句,为变量employee_sal赋值,具体语句如下:
DECLARE employee_sal INT DEFAULT 1000;
SET employee_sal=3500;
【代码说明】上述语句中首先声明了一个表示雇员工资的变量employee_sal,其默认值为1000, 然后设置该变量的值为3500。
(3)将查询结果赋值给变量,即将表t_employee中empno为“7566”记录中字段sal的值赋值给变量employee_sal。具体SQL语句如下:
SELECT sal INTO employee_sal FROM t_employee WHERE empno=7566;
【代码说明】在上述语句中通过SELECT...INTO语句将表t_employee里相应数据记录中字段sal 的值赋值给变量employee_sal。
3.2 操作条件
在高级编程语言中为了提高语言的安全性,提供了异常处理机制。对于MySQL软件,也提供了一种机制来提高安全性,即本节所要介绍的“条件”。条件的定义和处理可以用来定义在处理过程中遇到问题时相应的处理步骤。下面将介绍如何定义条件和设置处理程序。
1.定义条件
在MySQL中定义条件通过关键字DECLARE来实现,其语法形式如下:
DECLARE condition_name CONDITION FOR condition_value;
condition_value:
SQLSTATE[VALUE] sqlstate_value
| mysql_error_code
这个语句指定需要特殊处理的条件。它将一个名字和指定的错误条件关联起来。这个名字可以随后被用在DECLARE HANDLER语句中。除了SQLSTATE值,也支持MySQL错误代码。
在上述语句中,condition_name参数表示所要定义的条件名称;参数condition_value用来实现设置条件的类型;参数sqlstate_value和mysql_error_code都可以表示MySQL的错误。
例如ERROR 1146 (42S02)中,sqlstate_value值是42S02,mysql_error_code值是1146。
//定义"ERROR 1146 (42S02)"这个错误,名称为can_not_find
//方法一:使用sqlstate_value
DECLARE can_not_find CONDITION FOR SQLSTATE '42S02' ;
//方法二:使用mysql_error_code
DECLARE can_not_find CONDITION FOR 1146 ;
SQLSTATE 42S02为所有没有该表错误的总称:
mysql> SELECT * FROM FAN;
ERROR 1146 (42S02): Table 'fandb.FAN' doesn't exist
mysql> DROP TABLE FAN;
ERROR 1051 (42S02): Unknown table 'fandb.FAN'
Error: 1051 SQLSTATE: 42S02 (ER_BAD_TABLE_ERROR) Message: Unknown table '%s'
Error: 1109 SQLSTATE: 42S02 (ER_UNKNOWN_TABLE) -multi delete时没有该表 Message: Unknown table '%s' in %s
Error: 1146 SQLSTATE: 42S02 (ER_NO_SUCH_TABLE) Message: Table '%s.%s' doesn't exist
2.定义处理程序
在MySQL中定义条件通过关键字DECLARE ... HANDLER来实现,通过条件的定义和处理,可以在定义过程中,针对可能遇到的问题,做出相应的处理步骤。(也就是定义一个异常处理程序,指定当过程某条语句出错时,相应的采取什么操作)其语法形式如下:
DECLARE handler_action HANDLER FOR conition_value[,…] statement
handler_ action:
CONTINUE
| EXIT
condition_value:
SQLSTATE[VALUE] sqlstate_value
|condition_name
|SQLWARNING
|NOT FOUND
|SQLEXCEPTION
|mysql_error_code
注意:declare……handler语句必须出现在变量或条件声明的后面。
这个语句指定每个可以处理一个或多个条件的处理程序。如果产生一个或多个条件,指定的语句被执行。当某个错误(condition_value)发生时--->执行指定的语句(statement--记录错误信息),执行完之后再决定如何操作(handler_action)。
(1)handler_action
continue:继续执行当前的程序(接着执行出错的SQL的下一条语句);
exit: 当前程序终止(退出当前declare所在的begin end);
(2)statement
可以是单条语句或复合语句。
- condition_value参数指明错误类型
指明handler被何种条件触发;如果条件被触发,却没有handler被声明用于处理该条件,程序的进行将取决于条件类型。
condition_value该参数有6个取值:
sqlstate_value和mysql_error_code与条件定义中的是同一个意思,SQL语句过程中出现错误时返回的 5 个字符的错误代码。
condition_name是DECLARE定义的条件名称。
SQLWARNING表示所有以01开头的sqlstate_value值。
NOT FOUND表示所有以02开头的sqlstate_value值。
SQL EXCEPTION表示所有没有被SQLWARNING或NOT FOUND捕获的sqlstate_value值。statement表示一些存储过程或函数的执行语句。
例句:
//定义处理程序的几种方式
//方法一:捕获sqlstate_value
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info='CAN NOT FIND';
//方法二:捕获mysql_error_code
DECLARE CONTINUE HANDLER FOR 1146 SET @info='CAN NOT FIND';
//方法三:先定义条件,然后调用
DECLARE can_not_find CONDITION FOR 1146 ;
DECLARE CONTINUE HANDLER FOR can_not_find SET @info='CAN NOT FIND';
//方法四:使用SQLWARNING
DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';
//方法五:使用NOT FOUND
DECLARE EXIT HANDLER FOR NOT FOUND SET @info='CAN NOT FIND';
//方法六:使用SQLEXCEPTION
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';
第一种方法是捕获sqlstate_value值。如果遇到sqlstate_value值为42S02,执行CONTINUE操作,并且返回”CAN NOT FIND”信息。
第二种方法是捕获mysql_error_code值。如果遇到mysql_error_code值为1146,执行CONTINUE操作,并且返回”CAN NOT FIND”信息。
第三种方法是先定义条件,然后再调用条件。这里先定义can_not_find条件,遇到1146错误就执行CONTINUE操作。
第四种方法是使用SQLWARNING。SQLWARNING捕获所有以01开头的sqlstate_value值,然后执行EXIT操作,并且返回”ERROR”信息。
第五种方法是使用NOT FOUND。NOT FOUND捕获所有以02开头的sqlstate_value值,然后执行EXIT操作,并且返回”CAN NOT FIND”信息。
第六种方法是使用SQLEXCEPTION。SQLEXCEPTION捕获所有没有被SQLWARNING或NOT FOUND捕获的sqlstate_value值,然后执行EXIT操作,并且返回”ERROR”信息。
3.3 异常处理程序
1.单个异常处理程序
例子1:
CREATE TABLE test(s1 int,primary key (s1));
delimiter //
CREATE PROCEDURE small_mistake1()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
SET @x = 1;
INSERT INTO test.t VALUES (1);
SET @x = 2;
INSERT INTO test.t VALUES (1);
SET @x = 3;
END//
CALL small_mistake1()
Select @x
注意:@x是3,这表明MySQL被执行到程序的末尾。
如果DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; 这一行不在,第二个INSERT因PRIMARY KEY强制而失败之后,MySQL可能已经采取默认(EXIT)路径,并且SELECT @x可能已经返回2。
例子2:提交重复的数据记录(continue/exit)
mysql> DELIMITER $$
mysql> CREATE PROCEDURE small_mistake2()
-> BEGIN
-> DECLARE CONTINUE(EXIT) HANDLER FOR SQLSTATE '23000' ;
-> SET error = '23000'; #用来记录错误发生时的一些信息
-> select error;
-> SET error = '00000';
-> select error;
-> INSERT INTO TEAMS VALUES(2,27,'third'); #会出错的语句
-> SET error = '23001';
-> END $$
mysql> DELIMITER ;
begin end块里,定义declare……handler语句用来捕获错误(待命),select、set、select顺序执行,insert语句出错,SQLSTATE码23000,捕获,进行异常处理(赋值记录),(continue)结束后会继续执行出错的insert语句的下一条语句(exit)异常处理结束后不会继续执行错误语句后面的语句,直接退出begin end语句块。
2.多个异常处理程序
mysql> INSERT INTO TEAMS VALUES(2,27,'third',5);
ERROR 1136 (21S01): Column count doesn't match value count at row 1
mysql> DELIMITER $$
mysql> CREATE PROCEDURE small_mistake3()
-> BEGIN
-> DECLARE CONTINUE HANDLER FOR SQLSTATE '23000'
-> SET @error = '23000';
-> DECLARE CONTINUE HANDLER FOR SQLSTATE '21001'
-> SET @error = '21001';
-> INSERT INTO TEAMS VALUES(2,27,'third',5); #错误语句
-> END$$
mysql> DELIMITER ;
mysql> call small_mistake3();
mysql> select @error;
3.复合异常语句
delimiter $$
create procedure small_mistake4()
begin
declare duplicatekely condition for sqlstate '23000';
declare continue handler for duplicatekey begin select 'hello world';set @b=8;end;
set @a=1;
insert into table values(1);
set @a=2;
insert into table values(2);
set @a=3;
insert into table values(3);
end $$
delimiter ;
mysql> delete from table where id in (1,3);
mysql> call small_mistake4 ();
mysql> select @a,@b;
mysql> select * from table;
注意:DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END;
也就是说,当遇到SQLWARNING的问题时,进行的异常处理是begin end块,因为里面什么都没有,就类同于直接忽略。
3.4 使用游标
通过前面章节的知识可以知道,MySQL软件的査询语句可以返回多条记录结果,那么在表达式中如何遍历这些记录结果呢?MySQL软件提供了游标来实现。通过指定由SELECT语句返回的行集合(包括满足该语句的WHERE子句所列条件的所有行),由该语句返回完整的行集合叫作结果集。应用程序需要一种机制来一次处理结果集中的一行或连续的几行, 而游标通过每次指向一条记录完成与应用程序的交互。
游标可以看作一种数据类型,可以用来遍历结果集,相当于指针,或者是数组中的下标。处理结果集的方法可以通过游标定位到结果集的某一行,从当前结果集的位置搜索一行或一部分行或者对结果集中的当前行进行数据修改。
下面将介绍如何声明游标、打开游标、使用游标和关闭游标。
1.声明游标
在MySQL中声明游标通过关键字DECLARE来实现,其语法形式如下:
DECLARE cursor_name CURSOR FOR select_statement;
在上述语句中,cursor_name参数表示游标的名称,参数select_statement表示SELECT语句。因为游标需要遍历结果集中的每一行,增加了服务器的负担,导致游标的效率并不高效。如果游标操作的数据超过1万行,那么应该采用其他方式,另外如果使用了游标,还应尽量避免在游标循环中进行表连接的操作。
2.打开游标
在MySQL中打开游标通过关键字OPEN来实现,其语法形式如下:
OPEN cursor_name
在上述语句中,MySQL参数表示所要打开游标的名称。注意打开一个游标时,游标并不指向第一条记录,而是指向第一条记录的前边。
3.使用游标
在MySQL中使用游标通过关键字FETCH来实现,其语法形式如下:
FETCH cursor_name INTO var_name [,var_name] ...
在上述语句中,将参数游标cursor_name中SELECT语句的执行结果保存到变量参数var_name 中。变量参数var_name必须在游标使用之前定义。使用游标类似高级语言中的数组遍历,当第一次使用游标时,此时游标指向结果集的第一条记录。
4.关闭游标
在MySQL中关闭游标通过关键字CLOSE来实现,其语法形式如下:
CLOSE cursor_name
在上述语句中,cursor_name参数表示所要关闭游标的名称。
【实例14-5】此实例实现的功能为统计工资大于999的雇员人数,此功能可以直接通过WHERE 条件和COUNT()函数直接完成,此实例主要为演示游标的使用方法。下面将通过此实例的实现过程来演示如何使用游标,具体步骤如下:
(1)执行带有关键字DECLARE的语句,声明一个名为cursor_employee的游标,具体SQL语如下:
DECLARE cursor_employee CURSOR FOR select sal FOR t_employee;
Open cursor_employee;
【代码说明】在上述语句中打开名为cursor_employee的游标。
(2)执行带有关键字FETCH的语句,通过游标cursor_employee将查询结果赋值给变量,即将表t_employee表中所有记录中字段sal的值赋值给变量employee_sal具体SQL语句如下:
FETCH cursor_emplayee INTO employee_sal;
【代码说明】在上述语句中,将游标cursor_employee的查询结果赋值给变量employee sal。
(3)执行带有关键字CLOSE的语句,关闭游标cursor_employee,具体SQL语句如下:
CLOSE cursor_employee;
【代码说明】在上述语句中,关闭名为cursor_employee的游标。在具体使用游标时,游标必须在处理程序之前且在变量和条件之后声明,并且最后一定要关闭游标。经过上面的介绍,本实例完成的代码如下:
DELIMITER $
#创建存储过程
CREATE PROCEDURE employee_count (OUT NUM INTEGER)
BEGIN
#声明变量
DECLARE employee_sal INTEGER;
DECLARE flag INTEGER;
#声明游标
DECLARE cursor_employee CURSOR FOR SELECT sal FROM t_employee;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET flag = 1;
#设里初始变量
SET flag=0;
SET NUM=0;
#打开游标
OPEN cursor_employee;
#遍历游标指向的结果集
FETCH cursor_employee INTO employee_sal;
WHILE flag=0 DO
IF employee_sal >999 THEN SET num=num+1;
END IF;
FETCH cursor_emtployee INTO employee_sal;
END WHILE;
#关闭游标
CLOSE cursor_employee;
END
$
DELIMITER ;
上述实例创建了一个存储过程,并使用游标遍历结果集中的每一行,如果发现工资大于999, 则变量num加1,最后统计出符合条件的记录条数。如需调用此存储过程,可以使用以下方法:
#调用存储过程
mysql> CALL employee_count(@count);
mysql> select @count;
除了使用WHILE...END WHILE遍历结果集以外,游标的遍历还有以下几种方式。
- LOOP…END LOOP
- REPEAT…END REPEAT
使用LOOP循环遍历重写实例14-5如下:
DROP PROCEDURE IF EXISTS employee_count;
DELIMITER $
#创建存储过程
CREATE PROCEDURE employee_count(OUT NUM INTEGER)
BEGIN
#声明变量
DECLARE employee_sal INTEGER;
DECLARE flag INTEGER;
#声明游标
DECLARE cursor_employee
CURSOR FOR SELECT sal FROM t_employee;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET flag=1;
#设置结束标志
SET flag=0;
SET NUM=O;
#打开游标
OPEN cursor_employee;
#遍历游标
FETCH cursor_employee INTO employee_sal;
loop_label:LOOP
IF employee_sal >999 THEN SET num=num+l;
END IF;
FETCH cursor_employee INTO employee_sal;
if(flag=1) then LEAVE loop_label;
end if;
END LOOP;
#关闭游标
CLOSE cursor__employee;
END$
DELMITER ;
使用REPEAT…UNTIL循环遍历重写实例14-5如下:
DROP PROCEDURE IF EXISTS employee__count;
DELIMITER $
#创建存储过程
CREATE PROCEDURE employee_count(OUT NUM INTEGER)
BEGIN
#声明变量
DECLARE employee_sal INTEGER;
DECLARE flag INTEGER;
#声明游标
DECLARE cursor_employee CURSOR FOR SELECT sal FROM t_employee;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET flag=1;
#设置结束标志
SET flag=0;
SET NUM=0;
#打开游标
Open cursor_employee;
#遍历游标
FETCH cursor_employee INTO errployee_sal;
REPEAT
IF employee_sal >999 THEN SET num=num+l;
END IF;
FETCH cursor_employee INTO employee_sal;
UNTIL flag=1 END REPEAT;
#关闭游标
CLOSE cursor__employee;
END $
DELIMITER ;
以上的介绍说明了声明游标、打开游标、使用游标和关闭游标这几个概念,并通过一系列实例演示了游标的使用方法。由于使用游标需要把结果集中的记录一条条取出来处理,增加了服务器的负担,使用游标处理结果集效率比较低。所以如果可以不使用游标就尽量不要使用。
3.5 使用流程控制
流程控制语句主要用来实现控制语句的执行顺序,例如顺序、条件和循环。可以通过关键字IF和CASE来实现条件控制,关键字LOOP、WHILE和REPEAT实现循环控制。下面将介绍如何实现条件控制语句和循环控制语句。
1.条件控制语句
在MySQL软件中可以通过关键字IF和CASE来实现条件控制语句,IF语句具体进行条件控制时,根据是否满足条件,执行不同的语句;而对于CASE语句则可以实现更复杂的条件控制。下面将详细条件控制语句:
在MySQL中实现条件判断通过关键字lF来实现,其语法形式如下:
IF search_condition THEN statement_list
[ELSEIF search_condition THEN statement_list]…
[ELSE search_condition]
END IF
上述语句中search_condition参数表示条件的判断;参数statement_list表示不同条件的执行语句。在MySQL中实现条件判断还可以通过关键字CASE来实现,其语法形式如下:
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement list]...
[ELSE statement_list]
END CASE
在上述语句中,case_value参数表示条件判断的变量,参数when_value表示条件判断变量的值,参数statement_list表示不同条件的执行语句。
2.循环控制语句
在MySQL软件中可以通过关键字LOOP、WHILE和REPEAT来实现循环控制语句,其中后两个关键字用来实现带有条件的循环控制语句,即对于关键字WHILE,只有在满足条件的基础上才执行循环体,而关键字REPEAT,则是在满足的条件时退出循环体。
MySQL实现循环执行通过关键字LOOP来实现,其语法形式如下:
[begin_label:] LOOP
statement_list
END LOOP [end_label]
在上述语句中,begin_label和end_label参数分别表示循环开始和结束的标志,这两个标志必须相同,并且可以省略。关键字LOOP表示循环体的开始,END LOOP表示循环体的结束。参数 statement_list表示所执行的循环体语句。
对于循环语句,如果想实现退出正在执行的循环体,可以通过关键字LEAVE来实现,其语法形式如下:
LEAVE label
在上述语句中,参数label表示循环的标志#
在MySQL中实现循环执行还可以通过关键字WHILE来实现,不过其是带有条件控制的循环,即当满足条件是才执行循环体语句,具体语法形式如下:
[begin_label:] WHILE search_condition DO
statement_list
END WHILE [end_label]
在上述语句中,参数search_condition表示循环的执行条件,当满足该条件时才执行循环体 statement_Iist。
还可以通过关键字REPEAT来实现,其同样也是带有条件控制的循环,不过当满足条件则跳出循环体
语句,具体语法形式如下:
[begin_label:] REPEAT
statement_list
END REPEAT [end_label]
四、学习任务4:存储过程详解
4.1 存储过程的参数
存储过程根据需要可能会有输入、输出、输入输出参数,如果有多个参数用","分割开。MySQL存储过程的参数,共有三种参数类型,IN,OUT,INOUT:
IN参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值。
OUT参数的值可在存储过程内部被改变,并可返回。
INOUT参数的值调用时指定,并且可被改变和返回。
IN参数例子
DELIMITER $$
CREATE PROCEDURE in_param(IN p_in int)
BEGIN
SELECT p_in;
SET p_in=2;
SELECT p_in;
END;
$$
DELIMITER ;
#调用
SET @p_in=1;
CALL in_param(@p_in);
SELECT @p_in;
执行结果:
以上可以看出,p_in虽然在存储过程中被修改,但并不影响@p_in的值。
OUT参数例子
#存储过程OUT参数
DELIMITER $$
CREATE PROCEDURE out_param(OUT p_out int)
BEGIN
SELECT p_out;
SET p_out=2;
SELECT p_out;
END
$$
DELIMITER ;
#调用
SET @p_out=1;
CALL out_param(@p_out);
SELECT @p_out;
执行结果:
INOUT参数例子
#存储过程INOUT参数
DELIMITER $$
CREATE PROCEDURE inout_param(INOUT p_inout int)
BEGIN
SELECT p_inout;
SET p_inout=2;
SELECT p_inout;
END
$$
DELIMITER ;
#调用
SET @p_inout=1;
CALL inout_param(@p_inout) ;
SELECT @p_inout;
执行结果:
注意:用户变量一般以@开头
(滥用用户变量会导致程序难以理解及管理甚至报错) #在MySQL客户端使用用户变量
SELECT 'Hello World' into @x;
SELECT @x;
SET @y='Goodbye Cruel World';
SELECT @y;
SET @z=1+2+3;
SELECT @z;
执行结果:
MySQL注释方法:
#单行注释
-- 单行注释(特别注意,-- 后有个空格!!!)
/*多行注释*/
4.2 存储过程的控制语句
1、变量作用域
内部变量在其作用域范围内享有更高的优先权,当执行到end时,内部变量消失,不再可见了,在存储过程外再也找不到这个内部变量,但是可以通过out参数或者将其值指派给会话变量来保存其值。
#变量作用域
DELIMITER //
CREATE PROCEDURE proc()
BEGIN
DECLARE x1 VARCHAR(5) DEFAULT 'outer';
BEGIN
DECLARE x1 VARCHAR(5) DEFAULT 'inner';
SELECT x1;
END;
SELECT x1;
END
//
DELIMITER ;
#调用 CALL proc();
执行结果:
2、条件语句
(1)IF-THEN-ELSE语句(举例)
#条件语句IF-THEN-ELSE
DELIMITER //
CREATE PROCEDURE proc1(IN parameter int)
BEGIN
DECLARE var int;
SET var=parameter+1;
IF var=0 THEN
INSERT INTO table VALUES (17);
END IF ;
IF parameter=0 THEN
INSERT INTO table VALUES (18);
ELSE
INSERT INTO table VALUES (19);
END IF ;
END
//
DELIMITER ;
(2)CASE-WHEN-THEN-ELSE语句
#CASE-WHEN-THEN-ELSE语句
DELIMITER //
CREATE PROCEDURE proc2 (IN parameter INT)
BEGIN
DECLARE var INT;
SET var=parameter+1;
CASE var
WHEN 0 THEN
INSERT INTO t VALUES (17);
WHEN 1 THEN
INSERT INTO t VALUES (18);
ELSE
INSERT INTO t VALUES (19);
END CASE ;
END //
DELIMITER ;
(3)循环语句 WHILE-DO…END-WHILE
DELIMITER //
CREATE PROCEDURE proc3()
BEGIN
DECLARE var INT;
SET var=0;
WHILE var<6 DO
INSERT INTO t VALUES (var);
SET var=var+1;
END WHILE ;
END //
DELIMITER ;
(4)REPEAT...END REPEAT
此语句的特点是执行操作后检查结果
DELIMITER //
CREATE PROCEDURE proc4()
BEGIN
DECLARE v INT;
SET v=0;
REPEAT
INSERT INTO table VALUES(v);
SET v=v+1;
UNTIL v>=5
END REPEAT;
END //
DELIMITER ;
(5)LOOP...END LOOP
DELIMITER //
CREATE PROCEDURE proc5()
BEGIN
DECLARE v INT;
SET v=0;
LOOP_LABLE:LOOP
INSERT INTO table VALUES(v);
SET v=v+1;
IF v >=5 THEN
LEAVE LOOP_LABLE;
END IF;
END LOOP;
END //
DELIMITER ;
五、学习任务5:函数详解
(1)顺序结构
先来一个简单的,创建一个函数将'2009-06-23 00:00:00'这样格式的datetime时间转化为‘2009
年6月23日0时0分0秒’这样的格式:
DELIMITER $$
CREATE FUNCTION func1(gdate datetime)
RETURNS varchar(255)
BEGIN
DECLARE x VARCHAR(255) DEFAULT '';
SET x= date_format(gdate,'%Y年%m月%d日%h时%i分%s秒');
RETURN x;
END $$
DELIMITER ;
(2)分支结构
DELIMITER $$
CREATE FUNCTION func2(s VARCHAR(255),n INT)
RETURNS varchar(255)
BEGIN
IF(ISNULL(s))
THEN RETURN '';
ELSEIF CHAR_LENGTH(s) < n
THEN RETURN s;
ELSE
RETURN CONCAT(LEFT(s,n),'...');
END IF;
END $$
DELIMITER ;
解析:这是一个截取字符串的函数,参数有两个,一个是字符串s,一个是数字n,将字符串s保留前n位,如果字符串的个数小于n,则返回字符串s,如果大于n,则返回前n位后面加...。
调用:SELECT func2('abcdefghijklmnopqrstuvwxyz',5); 返回 'abced...'
- 循环结构
DELIMITER $$
CREATE FUNCTION func3(n INT)
RETURNS text
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE s TEXT DEFAULT '';
myloop:LOOP
SET i=i+1;
SET s = CONCAT(s,'*');
IF i > n
THEN LEAVE myloop;
END IF;
END LOOP myloop;
RETURN s;
END $$
DELIMITER ;
解析:产生n+1个*。 调用:SELECT func3(3); 返回 '*****'
六、学习任务6:查看存储过程和函数
存储过程和函数的操作包括创建存储过程和函数、查看存储过程和函数、更新存储过程和函数, 以及删除存储过程和函数。本节将详细介绍如何查看存储过程和函数。在MySQL软件中可以通过三种方式来查看存储过程和函数,分别为通过SHOW STATUS语句查看存储过程和函数状态信息、通过系统表information_schema.routines查看存储过程和函数详细信息和通过SHOW CREATE语句查看存储过程和函数定义信息。
5.1 SHOW PROCEDURE /FUNCTION STATUS语句查看存储过程状态信息
(1)当创建存储过程时,如果数据库中已经存在该存储过程,则会发生提示存储过程已经存在错误语句。
为了避免上述错误,对于有经验的用户,当在创建存储过程之前,需要查看MySQL软件中是否已经存在该标识符的存储过程。那么如何査看mySQL软件中已经存在的存储过程呢?在MySQL软件中查看已经存在的存储过程通过SQL语句SHOW PROCEDURE来实现,其语法形式如下:
SHOW PROCEDURE STATUS [LIKE'pattern'] \G
在上述代码中,关键字SHOW PROCEDURE STATUS表示实现查看存储过程功能,参数LIKE 'pattern'用来设置所要查询的存储过程名称。
执行SQL语句SHOW PROCEDURE STATUS,在数据库company里查询存储过程对象proce_employee_sal,具体步骤如下:
SHOW PROCEDURE STATUS LIKE 'proce_employee_sal'\G
(2)当在创建函数之前,需要査看MySQL软件中是否已经存在该标识符的函数。那么如何查看MySQL软件中已经存在的函数?在MySQL软件中查看己经存在函数通过SQL语句SHOW FUNCTION来实现,其语法形式如下:
SHOW FUNCTION STATUS [LIKE 'pattern'] \G
在上述代码中,关键字SHOWFUNCTION STATUS表示实现査看函数功能,参数LIKE‘pattem’用来设置所要查询的函数名称。
执行SQL语句SHOW FUNCTION STATUS,在数据库company中查询函数对象 func_employee_sal,具体如下:
SHOW FUNCTION STATUS LIKE'func_employee_sal \G
执行结果显示了所指定函数对象func_employee_sal的各种详细信息。
5.2 通过查看系统表information_schema.routines实现查看存储过程和函数信息
在MySQL软件中,在系统数据库information_schema中存在一个存储所有存储过程和函数信息的系统表routines,因此查询该表格的记录也可以实现查看存储过程和函数功能。关于系统表routines 的表结构如图所示。
执行SQL语句SELECT,查看系统表routines中的所有记录,具体SQL语句如下:
SELECT * FROM routines \G
执行结果显示了 MySQL软件中所有的存储过程和函数对象的详细信息,除了显示所有存储过程和函数对象外,还可以査询指定存储过程和函数的详细信息。通过系统表routines查询关于存储过程对象proce_employee_sal的信息,具体SQL语句如下:
SELECT* FROM ROUTINES WHERE SPECIFIC_NAME='func_employee_sal' \G
执行结果显示了所指定函数func_employee_sal的详细信息,与前面的方式相比,使用起来更加方便、灵活。
对于MySQL软件用户来说,很少使用语句“SHOW PROCEDURE”、“SHOW FUNCTION”和语句“SELECT * FROM routines\G”来查询存储过程和函数的详细信息,因为在MySQL软件中,随着时间的推移,数据库对象存储过程和函数肯定会增多,如果査询所有存储过程和函数的详细信息,将显示许多许多的信息,不便于找到所需的存储过程和函数的信息。
5.3 通过SHOW CREATE PROCEDURE/FUNCTION语句查看对应定义信息
(1)除了上述两种方式来査看存储过程对象外,对于有经验的用户,当在创建存储过程之前,还可以通过关键字SHOW CREATE PROCEDURE来査看存储过程定义信息,其语法形式如下:
SHOW CREATE PROCEDURE proce_name \G
在上述代码中,关键字SHOW CREATE PROCEDURE表示实现查看存储过程定义信息,参数 proce_name用来设置所要查询的存储过程名称。
执行SQL语句SHOW CREATE PROCEDURE,查询存储过程对象proce_employee_sal,具体SQL语句如下:
SHOW CREATE PROCEDURE proce_employee_sal\G
执行结果会显示了所指定存储过程对象proce_employee_sal的定义详细信息。
(2)还可以通过关键字SHOW CREATE FUNCTION来查看函数定义信息,其语法形式如下:
SHOW CREATE FUNCTION func_name \G
七、学习任务7:修改存储过程和函数
对于已经创建好的存储过程和函数,当使用一段时间后,就会需要进行一些定义上的修改。在 MySQL软件中,可以通过ALTER PROCEDURE语句实现修改存储过程,可以通过ALTER FUNCTION语句实现修改函数。
7.1 修改存储过程
在MySQL数据库管理系统中修改存储过程通过SQL语句ALTER PROCEDURE来实现,其语法形式如下:
ALTER PROCEDURE procedure_name [characteristic…]
在上述语句中,procedure_name参数表示所要修改存储过程的名字,而characteristic参数指定修改后存储过程的特性,与定义存储过程的该参数相比,取值只能是如下值:
|{CONTAINS SQL|NO SQL|READS SQL DATA|MODIFIES SQL DATA}
|SQL SECURITY {DEFINER|INVOKER}
|COMMENT 'string'
执行SQL语句ALTER TABLE修改表t_dqrt的名字为tab_dept,具体SQL语句如下:
ALTER TABLE t_dept RENAME tab_dept;
7.2 修改函数
在MySQL数据库管理系统中修改函数通过SQL语句ALTER FUNCTION来实现,其语法形式如下:
ALTER FUNCTION function_name[characteristic…]
在上述语句中,function_name参数表示所要修改函数的名字,而characteristic参数指定修改后函数的特性,与定义函数的该参数相比,取值只能是如下值:
|{CONTAINS SQL|NO SQL|READS SQL DATA|MODIFIES SQL DATA}
|SQL SECURITY {DEFINER|INVOKER}
|COMMENT 'string'
注意:所要修改函数必须在數据库中已经存在。
八、学习任务8:删除存储过程和函数
存储过程和函数的操作包括创建存储过程和函数、查看存储过程和函数、更新存储过程和函数,以及删除存储过程和函数。本节将详细介绍如何删除存储过程和函数。在MySQL软件中可以通过两种方式来删除存储过程和函数,分别为通过drop procedure/frunction语句和通过工具实现删除存储过程和函数。
(1)在MySQL中删除存储过程通过SQL语句DROP PROCEDURE来实现,其语法形式如下:
DROP PROCEDURE proce_name;
在上述语句中,关键字DROP PROCEDURE用来表示实现删除存储过程,proce_name参数表示所要删除的存储过程名称。
(2)在MySQL中删除存储过程函数通过SQL语句DROP FUNCTION来实现,其语法形式如下:
DROP FUNCTION func_name;
在上述语句中,关键字DROP FUNCTION用来表示实现删除函数,func_name参数表示所要删除的函数名称。