存储过程与存储函数

【应知部分】:

1.存储过程与存储函数概述

  存储过程与存储函数是MySQL自5.0版本之后开始支持的过程式数据库对象。它们作为数据库存储的重要功能,可以提高数据库的处理速度,同时也可以提高数据库编程的灵活性。

2. 存储过程的概念

   概念:

   存储过程是一组为了完成某特定功能的SQL语句集,其实质上就是一段存放在数据库中的代码,它可以由声明式语句(如CREATE、UPDATE和SELECT等语句)和过程式语句(如IF-THEN-ELSE控制结构语句)组成。这组语句经过编译后会存储在数据库中,用户只需通过指定存储过程的名字并给定参数(如果该存储过程带有参数),即可随时调用处理它,而不必重新编译,提高数据库操作语句的执行效率。

 

   优点:

(1)增强SQL语言的功能与灵活性。

(2)良好的封装性。

(3)高性能。

(4)可减少网络流量。

(5)保证数据库的安全性和数据的完整性。

3. 存储过程体的作用

   在存储过程体中可以使用各种SQL语句与过程式语句的组合,来封装数据库应用中复杂的业务逻辑和处理规则,以实现数据库应用的灵活编程。

   构造存储过程体的语法元素:

(1)局部变量

(2)SET语句

(3)SELECT…INTO语句

(4)流程控制语句

(5)游标

4. 存储函数的概念

   在MySQL中,存在一种与存储过程十分相似的过程式数据库对象——存储函数。它与存储过程一样,都是由SQL语句和过程式语句组成的代码片段,并且可以被应用程序和其他SQL语句调用。

   存储函数与存储过程的区别:

(1)存储函数不能拥有输出参数。存储过程拥有输出参数。

(2)可以直接对存储函数调用,不需要使用CALL语句,存储过程调用需要。

(3)存储函数中必须包含RETURN语句,此语句不允许出现在存储过程中。

【应会部分】:

一、 创建存储过程

语法:CREATE PROCEDURE sp_name([proc_parameter[,…]])

      [characteristic…]routine_body

其中,proc_parameter的格式为:

      [IN|OUT|INOUT]param_name type

      type的格式为:

      

Any valid MySQL data type

      characteristic的格式为:

      COMMENT ‘string’

      |LANGUAGE SQL

      |[NOT]DETERMINISTIC

      |{ CONTAINS SQL|NO SQL|READES SQL DATA|MODIFIES SQL DATA}

      |SQL SECURITY{DEFINER|INVOKER}

      routine_body的格式为:

      

Valid SQL routine statement

主要语法说明:(详细说明见P141

★sp_name:存储过程的名称,默认在当前数据库下创建。定义名字应该避免与MySQL内置函数相同。

★proc_parameter:存储过程参数列表。参数取名不要与数据表的列名相同。

characteristic:存储过程的某些特征设定。

routine_body:存储过程的主体部分,也称为存储过程体,其包含了在过程调用的时候必须执行的SQL语句。这个部分以BEGIN开始,以关键字END结束。

DELIMITER命令的语法格式:

  DELIMITER $$(略)

例题:

1)将MySQL结束符修改为两个感叹号“$$”

修改:mysql> delimiter $$

还原:mysql> delimiter ;

2)在数据库db_school中创建一个存储过程,用于实现给定表tb_student中一个学生的学号即可修改表tb_student中该学生的性别为一个指定的性别(使用该存储过程修改学生表中的学号、性别)。

mysql> use db_school;
mysql> delimiter $$
mysql> create procedure sp_update_sex(in sno int,in ssex nchar(2))

begin
update tb_student set sex=ssex where studentno=sno;
end $$

mysql> delimiter ;

注意:“ssex nchar(2)” 在Linux系统下的Mysql数据库管理系统中,由于字符集问题导致乱码的情况时有发生,大多是因为兼容性造成的,我们这里使用的是Unicode编码,能够表示全世界所有的字节。使用存储过程插入或更新的字符有可能出现乱码问题,为保证不出现乱码我们这里采用Unicode编码,即nchar或nvarchar。

查看存储过程状态你就会明白:

mysql>show procedure statusG

*************************** 1. row ***************************

                  Db: db_school

                Name: sp_update_sex

                Type: PROCEDURE

             Definer: root@localhost

            Modified: 2018-05-19 21:38:52

             Created: 2018-05-19 21:38:52

       Security_type: DEFINER

             Comment:

mysql>character_set_client: utf8

collation_connection: utf8_general_ci

Database Collation: latin1_swedish_ci

1 row in set (0.00 sec)

或查看指定的数据库“存储过程”。

show create procedure sp_update_sexG

*************************** 1. row ***************************

           Procedure: sp_update_sex

            sql_mode:

    Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_update_sex`(in sno int,in ssex nchar(1))

begin

update tb_student set sex=ssex where studentno=sno;

end

character_set_client: utf8

collation_connection: utf8_general_ci

Database Collation: latin1_swedish_ci

1 row in set (0.00 sec)

另外需要注意的是:如果你不是使用root用户创建的数据库存储过程,一定要去查看它是否有create routine权限。否则储存过程是无法创建的。

 

查看方法:以用户’root’@’localhost’为例

mysql> select Create_routine_priv from mysql.user

    -> where user='root' and host='localhost';

+-----------------------------------+

| Create_routine_priv     |

+-----------------------------------+

| Y                     |

+------------------------------------+

1 row in set (0.00 sec)

‘Y’说明拥有该权限。

 

【调用】储存过程怎样使用,我们这里使用以上创建的储存过程来验证。

mysql> select studentNo,sex

    -> from tb_student

    -> where studentNo='2013110101';

+------------------+---------+

| studentNo  | sex   |

+------------------+---------+

| 2013110101 ||

+------------------+---------+

1 row in set (0.00 sec)这里我们使用存储过程把学号为“2013110101”的同学的性别更新为“女”。

mysql> call sp_update_sex('2013110101',n'');

验证:

mysql> select studentNo,sex

      from tb_student

      where studentNo='2013110101';

+-------------------+----------+

| studentNo   | sex   |

+-------------------+----------+

| 2013110101  ||

+-------------------+----------+

1 row in set (0.00 sec)

验证成功!

 

授予普通用户拥有“存储过程或函数”的相关权限:(复习授权)

User表中的列

权限名称

权限的范围

Alter_routine_priv

ALTER ROUTINE

修改存储过程和函数

Create_routine_priv

CREATE ROUTINE

创建存储过程和函数

Execute_priv

EXECUTE

执行存储过程和函数

mysql> use mysql

mysql> grant ALTER ROUTINE,CREATE ROUTINE,EXECUTE on `db_school`.*

to 'bob'@'localhost' identified by '123456';

或者:

mysql> use mysql


mysql> create user 'bob1'@'localhosat' identified by '123456';


mysql>update user

set Create_routine_priv='Y',

Alter_routine_priv='Y',

Execute_priv:='Y'

where user='bob1' and host='localhost';

其它权限如果需要也必须开启!

二、存储过程

1)局部变量

作用:在存储过程体中可以设置局部变量,用来存储存储过程体中的临时结果。在mysql5.5中可以使用DECLARE语句来声明局部变量并同时还可以对该局部变量赋予一个初始值。

其语法格式为:

DECLARE var_name[ ,…] type [DEFAULT value]

语法说明:

var_name:用于指定局部变量的名称

type:用于声明局部变量的数据类型

DEFAULT子句:用于为局部变量指定一个默认值。若没有指定默认为空。

注意:

(1)局部变量只能在存储过程体的BEGIN…END语句块中声明。

(2)局部变量必须在存储过程体的开头处声明。

(3)局部变量的作用范围仅限于声明它的BEGIN…END语句块,其它语句块中的语句不可以使用它。

(4)局部变量不同于用户变量,两者的区别是:局部变量声明时,在其前面没有使用“@”符号,并且它只能声明它的BEGIN…END语句块中的语句所使用;用户变量在声明时,会在前面使用“@”符号,同时声明的用户变量存在于整个会话之中。

2)SET语句

在MySQL5.5中可以使用SET语句为局部变量赋值,其语法格式是:

SET var_name=expr[,var_name=expr];

例题:使用存储过程计算两个值的和

mysql>use db_school   #存储过程是数据库对象,因此必须选择一个数据库

mysql> delimiter $$

mysql>create procedure sp_sum(in a int,in b int)

begin

declare c int;

set c=a+b;

select c;

end $$

mysql> delimiter ;

mysql> call sp_sum(5,6);

+---- -------+

| c      |

+-----------+

|   11  |

+-----------+

1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

3)SELECT …INTO语句

   在MySQL中,可以使用SELECT…INTO语句把选定列的值直接存储到局部变量中,其语法格式是:

SELECT col_name[,…]INTO var_name[,…]table_expr

语法说明如下:

★col_name:用于指定列名。

★var_name:用于指定要赋值的变量名。

★table_expr:表示使用SELECT语句中的FROM子句及后面的语法部分。

注意:存储过程体中的SELECT…INTO语句返回的结果集只能有一行数据。

例题:创建一个存储过程,用于得到某个学生的籍贯。

mysql> delimiter $$

mysql> create procedure sp_native1(in sno int)

      begin

      declare na nchar(20);

      select native into na from tb_student

      where studentno=sno;

      select na;

      end$$       

mysql> delimiter ;

调用:

mysql> call sp_native1 ('2013110201');

+----------------+

| na        |

+----------------+

| 内蒙古   |

+----------------+

1 row in set (0.00 sec)

 

Query OK, 0 rows affected (0.00 sec)

例题:创建一个存储过程,用于得到某个指定学生的籍贯。

mysql> delimiter $$

mysql> create procedure sp_native2(inout sno int)

      begin

      declare na nchar(20);

      select native into na from tb_student

      where studentno=sno;

      select na;

      end$$    

mysql> delimiter ;

注意:调用前这里必须先赋值。

mysql> set @sno=2013110101;

mysql> call sp_native2 (@sno);

+-------------+

| na     |

+-------------+

| 山西   |

+-------------+

1 row in set (0.01 sec)

 

Query OK, 0 rows affected (0.01 sec)

【理解】:参数类型in、out、inout的区别!具体内容请参阅P141。

4)流程控制语句

   在MySQL 5.5中,可以在存储过程体中使用以下两类用于控制语句流程的过程式SQL语句。

(1)条件判断语句

   

常用的条件判断语句有IF-THEN-ELSE语句和CASE语句。其中,IF-THEN-ELSE语句可以根据不同的条件执行不同的操作,其语法格式为:

     IF search_condition THEN statement_list

         [ELESEIF search_condition THEN statement_list][ELSE statement_list]

     END IF

语法及使用说明如下:

★search_condition:用于指定判断条件。

★statement_list:用于表示包含了一条或多条的SQL语句。

★只有当判断search_condition为真时,才会执行相应的SQL语句。

★IF-THEN-ELSE语句不同于系统函数IF()。

     CASE语句在存储过程中的使用具有两种语法格式,分别是:

     CASE case_value

          WHEN when_value THEN statement_list

          [WHEN when_value THEN statement_list][ELSE statement_list]

     END CASECASE

         WHEN search_condition THEN statement_list

         [WHEN search_condition THEN statement_list]

         [ELSE statement_list]

     END CASE

语法说明如下:

★第一种语法格式中的case_value用于指定要判断的值或表达式,随后紧跟的是一系列WHEN-THEN语句块。其中,第一个WHEN-THEN语句块中的参数when_value用于指定要与case_value进行比较的值。倘若比较的值为真,则执行对应的statement_list中的SQL语句。如若每一个WHEN-THEN语句块中的参数when_value都不能与case_value相匹配,则会执行ELSE子句中指定的语句。该CASE语句最终会以关键字END CASE作为结束。

★第二种语法格式中的关键字CASE后面没有指定参数,而是在WHEN-THEN语句块中使用search_condition指定一个比较表达式。若表达式为真,则会执行对应的关键字THEN后面的语句。与第一种语法格式相比,这种语法格式能够实现更为复杂的条件判断,而且使用起来会更方便。

例题:新建存储过程用于实现插入不同的数据(1)。

use db_school

Create table table1(variable1 char(10));

 

DELIMITER $$

CREATE PROCEDURE proc1

(IN parameter1 INTEGER)

BEGIN

DECLARE variable1 CHAR(10);

IF parameter1 = 17 THEN

SET variable1 = 'birds';

ELSE

SET variable1 = 'beasts';

END IF;

INSERT INTO table1 VALUES (variable1);

END $$

DELIMITER ;

 

验证:

set @parameter1=18;

call proc1(@parameter1);

select * from table1;

例题:新建存储过程用于实现插入不同的数据(2)

Create table t(s1 int);

 

DELIMITER $$

CREATE PROCEDURE proc2(IN parameter int) 

begin

declare var int; 

set var=parameter+1; 

if var=0 then

insert into t values(17); 

end if; 

if parameter=0 then

update t set s1=s1+1; 

else

update t set s1=s1+2; 

end if; 

end $$ 

DELIMITER ;

验证:

Set @parameter=-1;

Call proc2(@parameter);

Select * from t;
Set @parameter=0;

Call proc2(@parameter);

Select * from t;

例题:新建存储过程用于实现插入不同的数据(3)CASE语句

Create table t1(s1 int);

 

DELIMITER $$ 

CREATE PROCEDURE proc3(in parameter int)

begin

declare var int; 

set var=parameter+1; 

case var 

when 0 then insert into t1 values(17); 

when 1 then insert into t1 values(18); 

else insert into t1 values(19); 

end case; 

end $$

DELIMITER ;

验证

set @parameter=0;

call proc3(@parameter);

select * from t1;

或者

DELIMITER  $$ 

CREATE PROCEDURE proc4(in parameter int) 

begin

declare var int; 

set var=parameter+1; 

case 

when var=0 then

insert into t1 values(30);

when var>0 then

insert into t1 values(40);

when var<0 then

insert into t1 values(50);

else

insert into t1 values(60);

end case;

end $$

DELIMITER ;

 

验证:

set @parameter=-1;

call proc3(@parameter);

select * from t1;

(2)循环语句

     常用的循环语句有WHILE语句、REPEAT语句和LOOP语句。

WHILE语句语法格式如下:

while条件 do

--循环体

End while

例题:新建存储过程用于实现插入不同的数据(4)-WHILE语句。

DELIMITER  $$

CREATE PROCEDURE proc5() 

begin

declare var int; 

set var=0; 

while var<6 do 

insert into t values(var); 

set var=var+1; 

end while; 

end $$

DELIMITER ;

验证:

delete from t;

call proc5;

select * from t;

REPEAT语句语法格式如下:

repeat

--循环体

until循环条件    

endrepeat;

注:它在执行操作后检查结果,而while则是执行前进行检查。

例题:新建存储过程用于实现插入不同的数据(5)-REPEAT语句。

DELIMITER $$ 

CREATE PROCEDURE proc6() 

begin  

declare v int; 

set v=0; 

repeat 

insert into t values(v); 

set v=v+1; 

until v>=5 

end repeat; 

end $$

DELIMITER ;

验证:

delete from t;

call proc6;

select * from t;

LOOP语句:

注:loop循环不需要初始条件,这点和while 循环相似,同时和repeat循环一样不需要结束条件, leave语句的意义是离开循环;

例题:新建存储过程用于实现插入不同的数据(6)-LOOP语句。

DELIMITER $$ 

CREATE PROCEDURE proc7() 

begin

declare v int; 

set v=0; 

LOOP_LABLE:loop 

insert into t values(v); 

set v=v+1; 

if v >=5 then

leave LOOP_LABLE; 

end if; 

end loop; 

end $$

DELIMITER ;

验证:

delete from t;

call proc7;

select * from t;

LABLES:标号:

标号可以用在begin repeat while 或者loop 语句前,语句标号只能在合法的语句前面使用。可以跳出循环,使运行指令达到复合语句的最后一步;

ITERATE(迭代)语句:

通过引用复合语句的标号,来从新开始复合语句。

例题:新建存储过程用于实现插入不同的数据(7)- ITERATE(迭代)语句。

DELIMITER $$ 

CREATE PROCEDURE proc10() 

begin

declare v int; 

set v=0; 

LOOP_LABLE:loop 

if v=3 then  

set v=v+1; 

ITERATE LOOP_LABLE; 

end if; 

insert into t values(v); 

set v=v+1; 

if v>=5 then

leave LOOP_LABLE; 

end if; 

end loop; 

end $$

DELIMITER ;

验证:

delete from t;

call proc10;

select * from t;

MySQL存储过程的查询:

1select name from mysql.proc where db=’数据库名’;

(2) show procedure status where db=’数据库名’;

(3) SHOW CREATE PROCEDURE 数据库.存储过程名 G

5)游标

   游标(cursor)就是游动的标识,通俗的这么说,一条sql取出对应n条结果资源的接口/句柄,就是游标,沿着游标可以一次取出一行;

 

使用场景:

1.只能用于存储过程或存储函数中,不能单独在查询操作中使用;

2.在存储过程或存储函数中可以定义多个游标,但是一个BEGIN...END

语句块中每一个游标的名字必须是唯一的;

3.游标不是一条SELECT语句,是被SELECT语句检索出来的结果集;

游标使用步骤:

1)声明游标:使用游标前,必须申明(定义)它,定义要使用的SELECT语句;

DECLARE 游标名称 CURSOR FOR SELECT语句(返回一行或多行的数据);

2)打开游标:使用游标前,打开游标

OPEN 游标名称;

3)读取数据:对于填有数据的游标,可根据需要取出数据;

FETCH 游标名 INTO 变量名1,...变量名n;

注:FETCH语句是将游标指向的一行数据赋给一些变量,这些变量的数目必须等于声明游标SELECT子句中选择列的数目,游标相当于一个指针,指向当前的一行数据;

  1. 关闭游标:CLOSE 游标名称

例题:在数据库db_school中创建一个存储过程,用于计算表tb_student中数据行的行数。

USE db_school;

Delimiter $$

Create procedure sp_sumrow(in rows int)

Begin

DECLARE sno CHAR;

DECLARE FOUND BOOLEAN DEFAULT TRUE;

DECLARE cur CURSOR FOR SELECT studentno from tb_student;

DECLARE CONTINUE HANDLER FOR NOT FOUND

SET FOUND=FALSE;

SET rows=0;

OPEN cur;

FETCH cur INTO sno;

WHILE FOUND DO

SET rows=rows+1;

FETCH cur INTO sno;

END WHILE;

CLOSE cur;

Select @rows;

End $$

Delimiter ;

 

验证:

Call sp_sumrow(@rows);

6)删除存储过程

mysql>drop procedure sp_sumrow;

三、存储函数

create function 存储函数名(参数 数据类型)

RETURES 数据类型

存储函数体

Return values 返回数值给存储函数体

语法:

CREATE FUNCTION sp_name([func_parameter[,...]])

RETURNS type

[characteristic ...] routine_body

Return

 

存储函数语法:

Create function 函数([函数参数[,….]])

Returns 返回类型

Begin

If(

Return (返回的数据)

Else

Return(返回的数据)

end if;

end;

例1:在数据库db_school中创建一个存储函数,要求该函数根据给定的学号返回学生的姓名;

-- 创建存储函数

DELIMITER  $$

CREATE FUNCTION  f_studentname(id INT )

RETURNS CHAR(80) 

Begin

RETURN (SELECT Studentname FROM tb_student  WHERE studentno=id );

END

$$

DELIMITER ;

Select f_studentname(‘2013110101’);

例2:在数据库db_school中出创建一个存储函数,要求该函数根据给定的学号返回学生的性别,

如果数据库中没有给定的学号,则返回‘没有该学生’;

DELIMITER  $$

CREATE FUNCTION search_sex1(sno char(10))

Returns char(2)

deterministic

Begin

Declare ssex char(2);

Select sex INTO ssex from tb_student where studentno=sno;

IF SSEX IS NULL THEN

RETURN(SELECT '没有该学生');

ELSE IF SSEX='' THEN

Return(Select '');

ELSE

Return(Select '');

END IF;

END IF;

end $$

Select search_sex1(‘2013110101’)$$

 

查看存储过程和函数

存储过程和函数创建以后,可以查看存储过程和函数的状态和定义。

通过SHOW STATUS语句来查看存储过程和函数的状态,也可以通过SHOW CREATE语句来查看存储过程和函数的定义。

通过查询information_schema数据库下的Routines表来查看存储过程和函数的信息

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

MySQL中可以通过SHOW STATUS语句查看存储过程和函数的状态。其基本语法形式如下:

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

其中,PROCEDURE参数表示查询存储过程;FUNCTION参数表示查询存储函数;

LIKE ' pattern '参数用来匹配存储过程或函数的名称。

SHOW  FUNCTION STATUS LIKE '% search_sex1%';

 

原文地址:https://www.cnblogs.com/Jackbk/p/12692082.html