11.存储过程与函数

目录

1、基本存储过程和函数

1、创建存储过程

2、调用存储过程

2、创建存储函数

1、创建存储函数

2、调用存储函数

3、变量的使用

1、定义变量

2、为变量赋值

4、定义条件和处理程序

1.定义条件

2、定义处理程序

5、光标的使用(MySQL中光标只能在存储过程和函数中使用。)

1.声明光标

2.打开光标

3.使用光标

4.关闭光标

游标使用案例:

6、流程控制的使用:

1.IF语句

2.CASE语句

3.LOOP语句

4、LEAVE语句

5.ITERATE语句

6.REPEAT语句

7.WHILE语句

7、查看存储过程和函数

1、使用SHOW STATUS 语查看存储过程和函数的状态

2.使用SHOW CREATE语查看存储过程和函数的定义除了SHOW   STATUS之外,

3. 从information_schema.Routines 表中查看存储过程和函数的信息

8、修改删除过程和函数

9、练习题


1、基本存储过程和函数

简单地说,存储过程就是一条或者多条 SQL 语的集合,可视为批文件,

但是其作用不仅限于批处理。本章主要介绍如何创建存储过程和存储函以及变量的使用,

如何调用、查看修改、删除存储过程和存储函数等。

存储程序可以分为存储过程和函数,MySQL中创建存储过程和函数使用的语句分别是

CREATE   PROCEDURE和CREATE   FUNCTION。

使用CALL语来调用存储过程,只能用输出变量返回值。函数可以从语句外调用(即通过引用函数名),也能返回标量值。

存储过程也可以调用其他存储过程。

1、创建存储过程

创建存储过程,需要使用CREATE PROCEDURE语句,基本语法格式如下:

CREATE PROCEDURE sp_name ( [proc_parameter] )

[characteristics ...] routine_body

CREATE PROCEDURE 为用来创建存储函数的关键字;

sp_name为存储过程的名称

proc_parameter为指定存储过程的参数列表,

列表形式如下:[IN | OUT| INOUT ] param_name type

其中,IN 表示输入参数,OUT 表示输出参数,INOUT 表示既可以输入也可以输出:

param_name表示参数名称 type表示参数的类型,该类型可以是MySQL 数据库中的任意类型

characteristics指定存储过程的特性,有以下取值:

LANGUAGE SQL:说明 routine_body 部分是由 SQL语句组成的,当前系统支持的语言

为SQL,

SQL是LANGUAGE特性的唯一值。

[NOT] DETERMINISTIC:指明存储过程执行的结果是否确定。

DETERMINISTIC表示结果是确定的。每次执行存储过程时,相同的输入会

得到相同的输出。

NOTDETERMINISTIC 表示结果是不确定的,相同的输入可能得到不同的输

出。

如果没有指定任意一个值,默认为NOT DETERMINISTIC。

{CONTAINS SQL | NO SQL | READS SQLDATA | MODIFIES SQL DATA} ;

指明子程序使用 SOL语句的限制。

CONTAINS SQL表明子程序包含SQL语句,但是不包含读写数据的语句:

NOSQL表明子程序不包含 SQL语句:

READS SOL DATA说明子程序包含读数据的语句:

MODIFIES SOLDATA表明子程序包含写数据的语句。默认情况下,系统会指定为

CONTAINSSOL。

SQL SECURITY{DEFINER | INVOKER}:指明谁有权限来执行。

DEFINER表示只有定义者才能执行。

INVOKER 表示拥有权限的调用者可以执行。默认情况下,系统指定为DEFINER COMMENT'string:注释信息,可以用来描述存储过程或函数

routine_body是SQL代码的内容可以用BEGINEND来表示SOL代码的开始和结束.

编写存储过程并不是件简单的事情,可能存储过程中需要复杂的 SQL语句,并且要有创建

存储过程的权限:但是使用存储过程将简化操作,减少冗余的操作步骤,同时,还可以减少

操作过程中的失误,提高效率,因此存储过程是非常有用的,而且应该尽可能地学会使用。

下面的代码演示了存储过程的内容,名称为AvgFruitPrice,返回所有水果的平均价格,

输入代码如下:

create procedure AvgFruitPrice()
begin
  select avg(f_price) as avgprice from fruits
end;

上述代码中此存储过程名为AvgFruitPrice使用CREATE PROCEDURE AvgFruitPrice()语句定义。

此存储过程没有参数,但是后面的()仍然需要。BEGIN和END语句用来限定存储过程体,过程本身仅是一个简单的 SELECT 语句 (AVG为求字段平均值的函数)。

举例:创建查看fruits表的存储过程,代码如下:

delimiter //          --表示将系统默认的sql结束符号 ;修改为 //
create procedure Proc()
begin
  select * from fruits;
end //
delimiter ;           --表示将系统刚修改的 //结束符修改成原来的 ;

调用存储过程

call  Proc();

这个存储过程和使用 SELECT 语句查看表的效果得到的结果是一样的,当然存储过程

也可以 是很多语句的复杂组合,其本身也可以调用其他的函数来组成更加复杂的操作。

“DELIMITER//”语的作用是将MySOL的结束符设置为//,

因为MySOL默认的语句结束符号为分号“;’,为了避免与存储过程中 SQL 语句结束符相冲突,需要使用DELIMITER改变存储过程的结束符,并以“END //”结束存储过程。存储过程定义完毕 之后再使用“DELIMITER ;”恢复默认结束符。DELIMITER也可以指定其他符号做为结束符。

举例2:创建名称为CountProc的存储过程,代码如下:

delimiter //
create procedure CountProc(OUT param1 INT) 
begin
  select count(*) into param1 from fruits; 
end // 
delimiter ;

当使用DELIMITER命令时,应该避免使用反斜杠(’\’)字符,因为反斜线是MySQL的

转义字符。

简单查询是否创建成功:

show PROCEDURE status like '%Proc%' \G;

2、调用存储过程

存储过程已经定义好了,接下来需要知道如何调用这些过程和函数。存储过程和函数有多种调用方法。

存储过程必须使用 CALL 语句调用,并且存储过程和数据库相关,如果要执行其他数据库中的存储过程,需要指定数据库名称,例如CALL dbname.procname。

存储函数的调用与MySOL中预定义的函数的调用方式相同。

本节介绍存储过程和存储函数的调用。

调用存储过程

存储过程是通过CALL语句进行调用的,语法如下:

CALL sp_name([parameter[;...]]

CALL语句调用一个先前用CREATE PROCEDURE创建的存储过程,

其中sp_name为存储过程名称,parameter为存储过程的参数。

定义名为CountProc1的存储过程,然后调用这个存储过程,代码执行如下:

定义存储过程:

delimiter // 
create procedure CountProc1 (IN sid INT, OUT num INT) 
begin 
  select count(*) into num from fruits where s_id = sid; 
end // 
delimiter ; 

调用存储过程:

call CountProc1 (101, @num); 

查看返回结果:

select @num; 

该存储过程返回了指定sd=101的水果商提供的水果种类,返回值存储在num变量中

使用SELECT查看,返回结果为3。

2、创建存储函数

1、创建存储函数

创建存储函数,需要使用CREATE FUNCTION语句,基本语法格式如下

CREATE FUNCTION func_name ( [funcparameter] )

RETURNS type

[characteristic ...] routine_body

CREATE FUNCTION为用来创建存储函数的关键字:

func_name表示存储函数的名称

funcparameter为存储过程的参数列表,参数列表形式如下:

{ IN | OUT | INOUT } param_name type

其中,IN 表示输入参数,OUT 表示输出参数,INOUT表示既可以输入也可以输出:

param name 表示参数名称;type 表示参数的类型,该类型可以是MySOL数据库中的任

意类型。

RETURNS type 语句表示函数返回数据的类型;

characteristic 指定存储函数的特性,取值与创建存储过程时相同,这里不再赘述。

举例:创建存储函数,名称为NameByZip,该函数返回SELECT语句的查询结果,

数值类型为字符串型,代码如下:

delimiter //
create function NameByZip() 
returns char(50)
return (select s_name from suppliers where s_call= '48075'); 
// 
delimiter ;

如果在存储函数中的RETURN语句返回一个类型不同于函数的RETURNS子句中指定类型的值,返回值将被强制为恰当的类型。比如,如果一个函数返回一个ENUM或SET值,但是RETURN 语句返回一个整数,对于SET成员集的相应的ENUM成员,从函数返回的值是字符串。

指定参数为IN、OUT或INOUT只对 PROCEDURE是合法的。(FUNCTION 中总是默认为IN参数)。

RETURNS 子只能对FUNCTION做指定,对函数而言这是强制的。它用来指定函数的返回类型,而且函数体必须包含一个RETURN value语句。

2、调用存储函数

在MySOL中,存储函数的使用方法与MySOL内部函数的使用方法是一样的。换言之,

用户自己定义的存储函数与MySOL内部函数是一个性质的。

区别在于,存储函数是用户自己定义的,而内部函数是MySQL的开发者定义的。

举例:

定义存储函数CountProc2,然后调用这个函数,代码如下:

delimiter // 
create function CountProc2 (sid int)
returns int
begin 
	return (select count(*) from fruits where s_id = sid); 
end // 
delimiter ; 

create function CountProc2 (sid int) --存储函数的参数默认是IN方向

调用存储函数:

select CountProc2(101);

可以看到,该例与上一个例子中返回的结果相同。虽然存储函数和存储过程的定义稍有不

同, 但可以实现相同的功能,读者应该在实际应用中灵活选择。

3、变量的使用

变量可以在子程序中声明并使用,这些变量的作用范围是在 BEGIN..END程序中,

本小节主要介绍如何定义变量和为变量赋值。

1、定义变量

在存储过程中使用DECLARE语句定义变量,语法格式如下:

DECLARE var_name [,var_name] ..... data_type [DEFAULT value] ;

var_name为局部变量的名称。DEFAULT value 子给变量提供一个默认值。

值除了可以被声明为一个常数之外,还可以被指定为一个表达式。

如果没有 DEFAULT 子句,初始值为NULL。

举例:

定义名称为myparam的变量,类型为INT类型,默认值为100,代码如下:

declare myparam int  default 100;

2、为变量赋值

定义变量之后,为变量赋值可以改变变量的默认值。 MySOL中使用 SET 语为变量赋值

语法格式如下:

SET var_name = expr [, var_nam = eexpr] ... ;

在存储程序中的SET语句是一般SET语的扩展版本。被参考变量可能是子程序内声明的

变量,或者是全局服务器变量,如系统变量或者用户变量。

在存储程序中的SET语作为预先存在的SET 语法的一部分来实现。这允许SET a=x,b=y....

这样的扩展语法。其中不同的变量类型(局域声明变量及全局变量)可以被混合起来.

这也允许把局部变量和一些只对系统变量有意义的选项合并起来。

举例:声明3个变量,分别为var1、var2和var3,数据类型为INT,使用SET为变量赋

值,代码如下:

declare var1, var2, var3 int;   --声明三个int类型的变量
set var1 = 10, var2 = 20;       --给其中的两个变量赋值数字常量
set var3 = var1 + var2;         --用一个表达式给第三个变量赋值

MySQL中还可以通过SELECT ... INTO为一个或多个变量赋值,语法如下:

SELECT col_name[,...] INTO var_name[,...] table_expr;

这个SELECT语法把选定的列直接存储到对应位置的变量。

col_name表示字段名称;

var_name表示定义的变量名称;

table_expr表示查询条件表达式,包括表名称和WHERE子句。

举例:声明变量fruitname和fruitprice,通过SELECT ... INTO语句查询指定记录并为变

量赋值,

代码如下:

declare fruitname char(50); 
declare fruitprice decimal(8,2);  

select f_name,f_price into fruitname, fruitprice 
from fruits where f_id ='a1'; 

4、定义条件和处理程序

特定条件需要特定处理。这些条件可以联系到错误,以及子程序中的一般流程控制。

定义条件是事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时应

当采取的处理方式,并且保证存储过程或函数在遇到警告或错误时能继续执行。这样可以增强存储程序处理问题的能力,避免程序异常停止运行。

本节将介绍使用 DECLARE 关键字来定义条件和处理程序。

1.定义条件

定义条件使用DECLARE语句,语法格式如下:

DECLARE condition_name CONDITION FOR [condition_type]

[condition type]:

SQLSTATE [VALUE] sqlstate_value | mysql_error_code

其中,condition_name 参数表示条件的名称;condition_type 参数表示条件的类型

sqlstate_value和mysql_error_code 都可以表示MySOL 的错误,

sqlstate_value 为长度为5的字符串类型错误代码,mysql_error_code 为数值类型错误代码。

例如:ERROR 1142(42000)中,sqlstate_value的值是42000,mysql_error_code的值是1142。

这个语句指定需要特殊处理的条件。它将一个名字和指定的错误条件关联起来。这个名字可以随后被用在定义处理程序的DECLARE HANDLER语句中.

举例:定义"ERROR 1148(42000)"错误,名称为command_not_allowed。

可以用两种不同的方法来定义,代码如下:

//方法一:使用sqlstate_value

declare command_not_allowed condition for sqlstate '42000';

//方法二:使用mysql_error_code

declare command_not_allowed condition for 1148 ;

2、定义处理程序

定义处理程序时,使用DECLARE语句的语法如下:

DECLARE handler_type HANDLER FOR condition_value [,...] sp_statement

handler type:

CONTINUE | EXIT | UNDO

condition value:

SQLSTATE [VALUE] sqlstate_value

| condition_name

| SQLWARNING

| NOT EOUND

| SQLEXCEPTION

| mysql_error_code

其中,

handler type 为错误处理方式,参数取 3个值:CONTINUE、EXIT 和UNDO

CONTINUE 表示遇到错误不处理,继续执行:

EXIT 遇到错误马上出:

UNDO表示遇到错误后撤回之前的操作,MySQL中暂时不支持这样的操作。

condition_value表示错误类型,可以有以下取值:

SQLSTATE [VALUE] sqlstate_value:包含5个字符的字符串错误值;

condition_name:表示DECLARE CONDITION定义的错误条件名称;

SQLWARNING:匹配所有以01开头的SQLSTATE错误代码

NOTFOUND:匹配所有以02开头的SOLSTATE错误代码·

SQLEXCEPTION:匹配所有没有被SOLWARNING或NOTFOUND 捕获的SOLSTATE错误代码。

mysql_error_code:匹配数值类型错误代码

sp_statement参数为程序语句段表示在遇到定义的错误时,需要执行的存储过程或函数

举例:定义处理程序的几种方式,代码如下:

//方法一:捕获sqlstate_value (错误值)

declare continue handler for sqlstate '42002' set @info='NO_SUCH_TABLE'; 

//方法二:捕获mysql_error_code (错误码)

declare continue handler for 1146 set @info=' NO_SUCH_TABLE '; 

//方法三:先定义条件,然后调用

declare no_such_table condition for 1146; 
declare continue handler for no_such_table set @info='NO_SUCH_TABLE '; 

//方法四:使用SQLWARNING (警告)

declare exit handler for sqlstate set @info='ERROR';  

//方法五:使用NOT FOUND (没有找到)

declare exit handler for not found set @info=' NO_SUCH_TABLE '; 

//方法六:使用SQLEXCEPTION(其他)

declare exit handler for sqlexception set @info='ERROR';  

上述代码是6种定义异常处理程序的方法

第一种方法是捕获 sqlstate_value 值。如果遇到sqlstate value 值为“42S02”,执行CONTINUE 操作,并且输出“NOSUCHTABLE”信息。

第二种方法是捕获mysql_error_code值。如果遇到MySQL error code值为1146,执行CONTINUE 操作,并且输出“NOSUCH TABLE”信息。

第三种方法是先定义条件,然后再调用条件。这里先定义no such table条件,遇到1146 错误就执行CONTINUE操作。

第四种方法是使用SQLWARNING。SQLWARNING捕获所有以01开头的sqlstate value值,然后执行EXIT操作,并且输出“ERROR”信息。

第五种方法是使用NOT FOUND。NOT FOUND 捕获所有以02开头的sqlstate_value值然后执行EXIT操作,并且输出“NOSUCH TABLE”信息。

第六种方法是使用SQLEXCEPTION。SOLEXCEPTION 捕获所有没有被SOLWARNING或NOTFOUND捕获的sqlstate value值,然后执行EXIT操作,并且输出“ERROR”信息。

举例:

定义条件和处理程序,具体执行的过程如下:

create table t (s1 int,primary key (s1)); 

delimiter //  
create procedure handlerdemo () 
	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 // 
delimiter ; 

/*调用存储过程*/

call handlerdemo();

/*查看调用过程结果*/

select @x; 

@x是1个用户变量,执行结果@x 等于 3,这表明 MySQL 被执行到程序的末尾。

如果“DECLARE CONTINUE HANDLER FORSQLSTATE23000 SET @x2=1;” 这1行不存在 第2个INSERT因PRIMARYKEY强制而失败之后MySOL可能已经采取默认(EXIT)路径并且 SELECT@x可能已经返回2。

”@var name”表示用户变量,使用 SET 语句为其赋值,用户变量与连接有关,一个客户端 定义的变量不能被其他客户端看到或使用。当客户端退出时,该客户端连接的所有变量将自动释放。

5、光标的使用(MySQL中光标只能在存储过程和函数中使用。)

查询语句可能返回多条记录,如果数据量非常大,需要在存储过程和储存函数中使用

光标 来逐条读取查询结果集中的记录。应用程序可以根据需要滚动或浏览其中的数据。

本节将介绍如何声明、打开、使用和关闭光标。

光标必须在声明处理程序之前被声明,并且变量和条件还必须在声明光标或处理程序之

前被声明。

1.声明光标

MySQL中使用DECLARE关键字来声明光标,其语法的基本形式如下:

DECLARE cursor_name CURSOR FOR select_statement

其中cursor_name参数表示光标的名称;

select_statement参数表示SELECT语句的内容返回一个用于创建光标的结果集。

举例:

声明名称为cursor_fruit的光标,代码如下:

declare cursor_fruit cursor for select f_name, f_price from fruits ; 

上面的示例中,

光标的名称为cur_fruit,SELECT语句部分从fruits表中查询出f_name和f_price字

段的值。

2.打开光标

打开光标的语法如下:

OPEN cursorname(光标名称)

这个语句打开先前声明的名称为cursorname的光标。

举例: Open cursor_fruit;

3.使用光标

使用光标的语法如下:

FETCH cursor_name INTO var_name [,var_name] ...{参数名称)

其中cursor_name参数表示光标的名称;

var_name参数表示将光标中的SELECT语句查询出来的信息存入该参数中,

var_name必须在声明光标之前就定义好。

举例:

使用名称为cursor_fruit的光标。将查询出来的数据存入fruit_name和fruit_price这两个变量中,

代码如下:

fetch cursor_fruit into fruit_name, fruit_price ; 

上面的示例中,将光标cursor_fruit中SELECT语句查询出来的信息存入fruit_name

和fruit_price中。 fruit_name和fruit_price必须在前面已经定义。

DECLARE fruit_name CHAR(50);

DECLARE fruit_price DECIMAL(8,2);

4.关闭光标

关闭光标的语法如下:

CLOSE cursor_name{光标名称]

这个语句关闭先前打开的光标如果未被明确地关闭,光标在它被声明的复合语句的末尾被关闭。

举例: CLOSE cursor_fruit;

例子:

delimiter // 
create procedure get_name_price() 
begin 
 
 declare fruit_name char(255);           
 declare fruit_price decimal(8,2);
 declare cursor_fruit cursor for select f_name,f_price  from fruits; 
 
 open cursor_fruit; 
 
 read_loop: loop 
	fetch cursor_fruit into  fruit_name,fruit_price;  
	select fruit_name,fruit_price;
 end loop;  
 
 close cursor_fruit; 
 
end  // 
delimiter ;

查看

游标使用案例:

首先创建一个学生表并插入一些数据:

create table students (id int ,name text); 
insert into students values (1,'zhangsan'),(2,'lisi'),(3,'wangwu'),(4,'zhaoliu'); 

delimiter // 
create procedure get_all_students() 
begin 
 declare done int default false; -- 定义游标是否结束的标志 
 declare s_id int;             -- 存储查询结果中的学生编号 
 declare s_name varchar(255); -- 存储查询结果中的学生姓名 
 declare cursor_students cursor for select id, name from students; -- 声明游标,查询表 student 中的所有数据 
 -- 打开游标 
 open cursor_students; 
 -- 遍历游标指向的结果集 
 read_loop: loop 
 -- 获取游标指向的当前行 
 fetch cursor_students into s_id, s_name;    ---遍历游标得到的实际数据值
 -- 如果游标到达结果集的末尾,则跳出循环 
 if done then 
 leave read_loop;  
 end if; 
 -- 输出当前行的值 
 select s_id, s_name; 
 end loop; 
 -- 关闭游标 
 close cursor_students; 
end // 
delimiter ;

查看

在上面的示例中,声明了一个名为 cursor_students 的游标,用于查询表 students 中的所有数据。打开游标后,使用 read_loop 循环体遍历查询结果集,并使用 FETCH 命令获取当前行的值存储到变量 s_id和 s_name 中,并输出到控制台。如果游标到达结果集的末尾,则跳出循环。当游标使用完毕后,需要使用 CLOSE 命令关闭游标。虽然游标为开发者提供了一种方便的查询结果集的方式,但由于其需要使用额外的内存和资源,

因此在使用时需要格外注意以下几个问题:

1,游标会影响性能,特别是在处理大型数据集时。不建议在大规模或高并发环境中使用游标,应该优先考虑使用其他方式,例如子查询和 JOIN 操作等。

2,游标只能在存储过程中使用,无法直接在 SQL 语句中使用。

3,游标的使用需要谨慎,因为如果没有正确关闭游标,会导致 MySQL 数据库占用大量的内存资源,甚至出现崩溃的情况。

通常情况下,游标适用于以下几个场景:

1,需要在存储过程中实现复杂查询逻辑的情况。

2,需要分批次处理大型数据集的情况。

3,需要对查询结果集进行逐行处理的情况。

6、流程控制的使用:

流程控制语句用来根据条件控制语句的执行。MySQL 中用来构造控制流程的语句

有:IF 语句、 CASE 语句、LOOP 语句、LEAVE 语、ITERATE 语、REPEAT语和WHILE语句。

每个流程中可能包含一个单独语句,或者是使用BEGIN...END构造的复合语句,构造可以被嵌套。

本节将介绍这些控制流程语句。

1.IF语句

IF语句包含多个条件判断,根据判断的结果为 TRUE或FALSE 执行相应的语句,语法格

式如下:

IF expr_condition THEN statement_list

[ ELSEIF expr_condition THEN statement list] ...

[ ELSE statement list]

END IF

IF实现了一个基本的条件构造。如果expr_condition 求值为真(TRUE),相应的SQL语句

列表被执行

如果没有expr_condition 匹配 则ELSE子句里的语列表被执行statement_list可以包括一

个或多个语句。

注意:MySQL中还有一个IF()函数,它不同于这里描述的IF 语句。

举例:IF语句的示例,代码如下:

if val is null then select 'val is NULL'; 
else select 'val is not NULL';
end if;

举例

delimiter //
create procedure doiterate1() 
begin
declare val int;
if val is null then select 'val is NULL'; 
else select 'val is not NULL';
end if;
end //
delimiter ;

调用

call doiterate1();

该示例判断val值是否为空,如果val值为空,输出字符串“valisNULL”;

否则输出字符串“val is not NULL”。IF语都需要使用ENDIF来结束。

2.CASE语句

CASE是另一个进行条件判断的语句,该语句有2种语句格式,第1种格式如下:

CASE case_expr

WHEN when_value THEN statement_list

[WHEN when_value THEN statement_list]

[ELSE statement_list]

END CASE

其中,case_expr 参数表示条件判断的表达式,决定了哪一个 WHEN 子句会被执行:

when_value参数表示表达式可能的值如果某个when_value表达式与case_expr表达式结

果相同,

则执行对应THEN关键字后的 statement_list 中的语句:

statement_list 参数表示不同when_value值的执行语句。

使用CASE流程控制语句的第1种格式,判断val值等于1、等于2,或者两者都不等,语

句如下:

delimiter //
create procedure doiterate3() 
begin
declare val int;
case val
  when 1 then select 'val is 1'; 
  when 2 then select 'val is 2'; 
  else select 'val is not 1 or 2'; 
end case;
end //
delimiter ;

调用:

call doiterate3();

CASE语句的第2种格式如下:

case val
when expr_condition then  statement_list; 
[when expr_condition then statement_list]; 
else statement_list; 
end case;

case val
when expr_condition then  statement_list; 
[when expr_condition then statement_list]; 
else statement_list; 
end case;

其中,expr_condition 参数表示条件判断语句;statement_list 参数表示不同条件的执

行语句。

该语句中,WHEN语句将被逐个执行,直到某个expr_condition表达式为真,

则执行对应THEN关键字后面的statement_list语。如果没有条件匹配,ELSE子里的语

句被执行。

这里介绍的用在存储程序里的CASE语与“控制流程函数”里描述的SQL CASE表达式的

CASE语句

有轻微不同。这里的 CASE 语不能有ELSE NULL字句,并且用END CASE替代END来终

止。

使用CASE流程控制语句的第2种格式,判断val是否为空、小于0、大于0或者等于0,

语句如下:

delimiter //
create procedure doiterate4() 
begin
declare val int default 2;
case val
when val < 2 then select 'val is less 2'; 
when val > 2 then select 'val is greater than 2'; 
else select 'val is 2 '; 
end case;
end //
delimiter ;

调用

call doiterate4();

3.LOOP语句

LOOP循环语句用来重复执行某些语句,与F和CASE语相比,LOOP 只是创建一个循环

操作的过程,

并不进行条件判断。LOOP 内的语句一直重复执行直到循环被退出,跳出循环过程,使用LEAVE子句,LOOP语句的基本格式如下:

[loop_label:] LOOP

statement_list

END LOOP [loop_label

loop_label表示LOOP 语句的标注名称,该参数可以省略;

statement_list 参数表示需要循环执行的语句。

举例:使用LOOP语句进行循环操作,id值小于等于10之前,将重复执行循环过程,代

码如下:

delimiter // 
create procedure testproc4()
begin 
declare id int default 0; 
add_loop: loop 
 set id = id + 1; 
 if id >= 10 THEN LEAVE add_loop; 
 end if; 
end loop add_loop; 
select id ;
end //
delimiter ;

该示例循环执行id加1的操作。当id值小于10时,循环重复执行,

当d 值大于或者等于10时,使用LEAVE语句退出循环。

LOOP循环都以END LOOP结束

4、LEAVE语句

LEAVE语句用来退出任何被标注的流程控制构造,LEAVE语句基本格式如下:LEAVE  label

其中,label参数表示循环的标志。LEAVE和BEGIN...END或循环一起被使用

举例:使用LEAVE语句退出循环,代码如下:

add_num: LOOP

SET @count=@count+1;

IF @count=50 THEN LEAVE add_num ;

END LOOP add_num ;

该示例循环执行count加1的操作。当count的值等于50时,使用LEAVE语句跳出循环。

5.ITERATE语句

ITERATE语句将执行顺序转到语句段开头处,语句基本格式如下:ITERATE label

ITERATE只可以出现在LOOP、REPEAT和WHILE语内。ITERATE的意思为“再次循环”,

label参数表示循环的标志。ITERATE语句必须跟在循环标志前面。

举例:

ITERATE语句示例,代码如下:

delimiter // 
create procedure doiterate2() 
begin 
declare p1 int default 0; 
my_loop: loop 
set p1= p1 + 1;                
if p1 < 10 then iterate my_loop; 
elseif p1 > 20 then LEAVE my_loop; 
end if;  
select 'p1 is between 10 and 20',p1; 
end loop my_loop; 
end //
delimiter ;

执行过程是:

p1=0,如果p1的值小于10时,重复执行p1加1操作;

当p1大于等于10并且小于20时,打印消息“plis between 10and 20”;

当p1大于20时,退出循环。

6.REPEAT语句

REPEAT语句创建一个带条件判断的循环过程,每次语句执行完毕之后,

会对条件表达式进行判断,如果表达式为真,则循环结束:否则重复执行循环中的语

句。

REPEAT 语句的基本格式如下:

[repeat_label:] repeat 
statement_list 
UNTIL expr_condition 
end repeat [repeat_label] 

其中repeat_label为REPEAT语句的标注名称,该参数可以省略;

REPEAT语内的语句或语句群被重复,直至exprcondition为真。

举例:

REPEAT语句示例,id值小于等于10之前,将重复执行循环过程,代码如下:

delimiter //
create procedure doiterate5() 
begin
declare id int default 0;
repeat
set id = id +1;
until id >=10
end repeat;
end //
delimiter ;

该示例循环执行id加1的操作。

当id值小于10时,循环重复执行;

当id值大于或者等于10时,使用LEAVE语句退出循环。

REPEAT循环都以END REPEAT结束。

7.WHILE语句

WHILE语句创建一个带条件判断的循环过程与REPEAT不同,WHILE在执行语句时先对

指定的表达式进行判断,如果为真,则执行循环内的语句,否则退出循环。

WHILE 语句的基本格式如下:

[while_label:] WHILE expr_condition DO

statement_list

END WHILE [while_label]

while_label为WHILE语句的标注名称;expr_condition 为进行判断的表达式如果表达式结果为真,

WHILE语句内的语句或语句群被执行,直至expr_condition为假,退出循环。

举例:WHILE语句示例,id值小于等于10之前,将重复执行循环过程,代码如下:

delimiter //
create procedure doiterate6() 
begin
declare i int default 0; 
while i < 10 do 
set i = i + 1; 
end while; 
end //
delimiter ;

7、查看存储过程和函数

MySQL存储了存储过程和函数的状态信息用户可以使用SHOW STATUS语或SHOW CREATE

语句来查看,也可直接从系统的information schema数据库中查询。本节将通过实例来介绍这3种方法。

1、使用SHOW STATUS 语查看存储过程和函数的状态

SHOW STATUS 语句可以查看存储过程和函数的状态,其基本语法结构如下:

SHOW { PROCEDURE | FUNCTION } STATUS [ LIKE 'pattern']

这个语句是一个MySOL的扩展。它返回子程序的特征,如数据库、名字、类型、创建

者及创建和修改日期。如果没有指定样式,根据使用的语句,所有存储程序或存储函数的信息

都被列出。

PROCEDURE和FUNCTION分别表示查看存储过程和函数;LIKE语句表示匹配存储过程或函数的名称。

举例:SHOW STATUS语句示例,代码如下:

show procedure status like 'C%'\G 

“SHOW PROCEDURE STATUS LIKE C%\G”语取数据库中所有名称以字母“C“开头

的存储过程的信息。通过上面的语句可以看到:这个存储函数所在的数据库为 test、存储函数的名

称为CountProc等一些相关信息。

2.使用SHOW CREATE语查看存储过程和函数的定义除了SHOW   STATUS之外,

MySQL还可以使用SHOWCREATE 语句查看存储过程和函数的状态。

SHOW CREATE { PROCEDURE | FUNCTION} sp_name

这个语句是一个MySOL的扩展。类似于SHOW CREATE TABLE,它返回一个可用来重

新创建已命名子程序的确切字符串。PROCEDURE和FUNCTION分别表示查看存储过程和函数;

sp_name参数表示匹配存储过程或函数的名称。

举例:SHOW CREATE语句示例,代码如下:

show create function test.CountProc2 \G ;

执行上面的语句可以得到存储函数的名称为CountProc2,sqlmode为SQL的模式,

CreateFunction为存储函数的具体定义语句,还有数据库设置的一些信息。

3. 从information_schema.Routines 表中查看存储过程和函数的信息

MySOL中存储过程和函数的信息存储在information_schema数据库下的Routines表中

可以通过查询该表的记录来查询存储过程和函数的信息。其基本语法形式如下:

SELECT * FROM information_schema.Routines

WHERE ROUTINE_NAME='sp_name'

其中,ROUTINENAME字段中存储的是存储过程和函数的名称;

sp_name参数表示存储过程或函数的名称。

查询所有存储过程:

select * from information_schema.Routines 
where ROUTINE_TYPE = 'procedure' \G

查询所有的存储函数:

select * from information_schema.Routines
where ROUTINE_TYPE = 'FUNCTION' \G

举例:从Routines表中查询名称为CountProc的存储函数的信息,代码如下:

select * from information_schema.Routines 
where ROUTINE_NAME='CountProc2' and ROUTINE_TYPE = 'FUNCTION' \G ;

在information schema数据库下的 Routines 表中,存储所有存储过程和函数的定义。

使用SELECT语句查询 Routines 表中的存储过程和函数的定义时,一定要使用ROUTINE_NAME 字段指定存储过程或函数的名称。否则,将查询出所有的存储过程或函数的定义。

如果有存储过程和存储函数名称相同则需要同时指定ROUTINE_TYPE字段表明查询的是哪种类型的存储程序。

8、修改删除过程和函数

使用ALTER语句可以修改存储过程或函数的特性,

ALTER {PROCEDURE | FUNCTION) sp_name [characteristic ...]

其中,sp_name参数表示存储过程或函数的名称

characteristic 参数指定存储函数的特性可能的取值有:

CONTAINS SQL表示子程序包含SQL语句,但不包含读或写数据的语句。

NOSQL 表示子程序中不包含 SQL语句。

READS SQL DATA 表示子程序中包含读数据的语句

MODIFIES SQL DATA表示子程序中包含写数据的语句

SQL SECURITY { DEFINER | INVOKER }指明谁有权限来执行

DEFINER表示只有定义者自己才能够执行。

INVOKER表示调用者可以执行

COMMENT ‘string’表示注释信息

修改存储过程使用ALTER PROCEDURE 语句,修改存储函数使用ALTER FUNCTION语句。

但是,这两个语句的结构是一样的,语句中的所有参数也是一样的。而且,它们与创建存储过程或函数的语句中的参数也是基本一样的。

加注释:

ALTER PROCEDURE test_proc COMMENT '这是一个测试存储过程';

ALTER PROCEDURE testProc4 COMMENT '这是一个测试存储过程';

修改安全性:

将读写权限改为MODIFIES SQL DATA,并指明调用者可以执行,代码如下:

ALTER PROCEDURE testProc4

MODIFIES SQL DATA

SQL SECURITY INVOKER ;

举例:修改存储过程CountProc的定义。将读写权限改为MODIFIES SQL DATA,

并指明调用者可以执行,代码如下:

alter procedure CountProc
modifies sql data
sql SECURITY INVOKER ; 

执行代码,并查看修改后的信息。结果显示如下:

//查询修改后的CountProc表信息

select SPECIFIC_NAME,SQL_DATA_ACCESS,SECURITY_TYPE 
from information_schema.Routines
where ROUTINE_NAME='CountProc'  and ROUTINE_TYPE='PROCEDURE'; 

结果显示,存储过程修改成功。从查询的结果可以看出,

访问数据的权限(SQL DATAACCESS)已经变成MODIFIES SQL DATA,

安全类型(SECURITY TYPE)已经变成了INVOKER。

举例2:修改存储函数CountProc的定义。将读写权限改为READS SQL DATA,

并加上注释信息“FIND NAME”,代码如下:

alter function CountProc2
reads sql data
comment 'FIND NAME' ; 

执行代码,并查看修改后的信息。结果显示如下:

//查询修改后f表的信息

select SPECIFIC_NAME,SQL_DATA_ACCESS,ROUTINE_COMMENT 
from information_schema.Routines 
where ROUTINE_NAME='CountProc2' and ROUTINE_TYPE = 'FUNCTION' ; 

存储函数修改成功。从查询的结果可以看出,

访问数据的权限 (SQL DATA ACCESS)已经变成READS SQLDATA,

函数注释(ROUTINE COMMENT)已经变成了FIND NAME

删除存储过程和函数,可以使用 DROP 语句,其语法结构如下

DROP (PROCEDURE | FUNCTION ) [ IF EXISTS ] sp_name

这个语句被用来移除一个存储过程或函数。sp_name为要移除的存储过程或函数的名称 IF EXISTS 子句是一个MYSQL 的扩展。如果程序或函数不存储,它可以防止发生错误产 生一个用SHOW WARNINGS 查看的警告。

举例:

删除存储过程和存储函数,代码如下:

drop procedure CountProc; 
drop function CountProc2; 

语句的执行结果如下:

上面语句的作用就是删除存储过程CountProc和存储函数CountProc2。

9、练习题

注意事项:

疑问1:MySQL存储过程和函数有什么区别?

在本质上它们都是存储程序。函数只能通过return语句返回单个值或者表对象;

而存储过程不允许执行 return 语句,但是可以通过 out 参数返回多个值。

函数限制比较多,不能用临时表,只能用表变量,还有一些函数都不可用等等;

而存储过程的限制相对就比较少。函数可以嵌入在SQL语句中使用,

可以在SELECT语句中作为查询语句的一个部分调用;

而存储过程一般是作为一个独立的部分来执行。

疑问2:存储过程中的代码可以改变吗?

目前,MvSOL 还不提供对已存在的存储过程代码的修改,如果必须要修改存储过程,必须使用DROP语句删除之后,再重新编写代码,或者创建一个新的存储过程。

疑问3:存储过程中可以调用其他存储过程吗?

存储过程包含用户定义的SQL语句集合,可以使用CALL 语句调用存储过程,当然在存储过程中也可以使用 CALL 语句调用其他存储过程,但是不能使用 DROP 语删除其他存储过程。

疑问4:存储过程的参数不要与数据表中的字段名相同

在定义存储过程参数列表时,应注意把参数名与数据库表中的字段名区别开来,否则将出现无法预期的结果。

疑问5:存储过程的参数可以使用中文吗?

一般情况下,可能会出现存储过程中传入中文参数的情况,例如某个存储过程根据用

户的名字查找该用户的信息,传入的参数值可能是中文。这时需要在定义存储过程的时候,在后面加上character set gbk 不然调用存储过程使用中文参数会出错比如定义userInfo存储过程代码如下:

CREATE PROCEDURE useInfo(IN u_name VARCHAR(50) character set gbk, OUT

u_age INT);

练习题:

(1)写个HelloWord的存储过程和函数

存储过程

delimiter //
create procedure helloword(out h char(50))
begin
	declare h1 char(50);
	set h1 = 'helloword';
	select h1 into h;
end //
delimiter ;

call helloword(@h);
select @h;

存储函数

delimiter //
create function hw()
returns char(50)
return ('helloword');
//
delimiter ;

select hw();

(2)写一个完整的包括参数、变量、变量赋值、条件判断、UPDATE语句、SELECT返回结果

集的存储过程。

delimiter //
create procedure p2(out h char(50))
begin
	declare h1 int(50);
	set h1 = 1;
	if h1 = 1 then select 'h1 is 1'; 
	else select 'h1 is not 1';
	end if;
	select h1 into h;
end //
delimiter ;

call p2(@a);
select @a;

(3)创建一个执行动态SOL的存储过程。

delimiter // 
create procedure pp(in sid int,out num int)
begin 
  select count(*) into num from fruits where s_id = sid; 
end // 
delimiter ; 

call pp(101, @n); 
select @n; 
call pp(103, @nn); 
select @nn; 

(4)创建实现功能相同的存储函数,比较他们之间的不同点在什么地方?

delimiter //
create function p1(n int)
returns int
begin
	declare var int;
	set var = 0;
	while n>0 do
		set var = var + n;
		set n = n -1;
	end while;
	return var;
end //
delimiter ;

select p1(5);
select p1(6);

delimiter //
create function p4(n int)
returns int
begin
	declare var int;
	set var = 0;
	if n>0  then set 
	 var = var + n;
	 set n = n -1;
	end if;
	return var;
end //
delimiter ;

select p4(5);
select p4(6);

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值