mysql 存储过程 procedure

我们大家都知道MySQL 存储过程是从 MySQL 5.0 开始逐渐增加新的功能。存储过程在实际应用中也是优点大于缺点。不过最主要的还是执行效率和SQL 代码封装。特别是 SQL 代码封装功能,如果没有存储过程。

在外部程序访问数据库时(例如 PHP),要组织很多 SQL 语句。

特别是业务逻辑复杂的时候,一大堆的 SQL 和条件夹杂在 PHP 代码中,让人不寒而栗。现在有了 MySQL 存储过程,业务逻辑可以封装存储过程中,这样不仅容易维护,而且执行效率也高。

一、MySQL 创建存储过程

“pr_add” 是个简单的 MySQL 存储过程,这个MySQL 存储过程有两个 int 类型的输入参数 “a”、“b”,返回这两个参数的和。

 
 
  1. drop procedure if exists pr_add;  

计算两个数之和

 
 
  1. create procedure pr_add   
  2. (   
  3. a int,   
  4. b int   
  5. )   
  6. begin   
  7. declare c int;   
  8. if a is null then   
  9. set a = 0;   
  10. end if;   
  11. if b is null then   
  12. set b = 0;   
  13. end if;   
  14. set c = a + b;   
  15. select c as sum;   
  16. /*   
  17. return c;  

不能在 MySQL 存储过程中使用。return 只能出现在函数中。

 
 
  1. */   
  2. end;  

二、调用 MySQL 存储过程

 
 
  1. call pr_add(10, 20);  

执行 MySQL 存储过程,存储过程参数为 MySQL 用户变量。

 
 
  1. set @a = 10;   
  2. set @b = 20;   
  3. call pr_add(@a, @b);  

三、MySQL 存储过程特点

创建 MySQL 存储过程的简单语法为:

 
 
  1. create procedure 存储过程名字()   
  2. (   
  3. [in|out|inout] 参数 datatype   
  4. )   
  5. begin   
  6. MySQL 语句;   
  7. end;  

MySQL 存储过程参数如果不显式指定“in”、“out”、“inout”,则默认为“in”。习惯上,对于是“in” 的参数,我们都不会显式指定。

1. MySQL 存储过程名字后面的“()”是必须的,即使没有一个参数,也需要“()”

2. MySQL 存储过程参数,不能在参数名称前加“@”,如:“@a int”。下面的创建存储过程语法在 MySQL 中是错误的(在 SQL Server 中是正确的)。 MySQL 存储过程中的变量,不需要在变量名字前加“@”,虽然 MySQL 客户端用户变量要加个“@”。

 
 
  1. create procedure pr_add   
  2. (   
  3. @a int, -- 错误   
  4. b int -- 正确   
  5. )  

3. MySQL 存储过程的参数不能指定默认值。

4. MySQL 存储过程不需要在 procedure body 前面加 “as”。而 SQL Server 存储过程必须加 “as” 关键字。

 
 
  1. create procedure pr_add   
  2. (   
  3. a int,   
  4. b int   
  5. )   
  6. as -- 错误,MySQL 不需要 “as”   
  7. begin   
  8. mysql statement ...;   
  9. end;  

5. 如果 MySQL 存储过程中包含多条 MySQL 语句,则需要 begin end 关键字。

 
 
  1. create procedure pr_add   
  2. (   
  3. a int,   
  4. b int   
  5. )   
  6. begin   
  7. mysql statement 1 ...;   
  8. mysql statement 2 ...;   
  9. end;  

6. MySQL 存储过程中的每条语句的末尾,都要加上分号 “;”

 
 
  1. ...   
  2. declare c int;   
  3. if a is null then   
  4. set a = 0;   
  5. end if;   
  6. ...   
  7. end;  

7. MySQL 存储过程中的注释。

 
 
  1. /*   
  2. 这是个   
  3. 多行 MySQL 注释。   
  4. */   
  5. declare c int; -- 这是单行 MySQL 注释 (注意 -- 后至少要有一个空格)   
  6. if a is null then # 这也是个单行 MySQL 注释   
  7. set a = 0;   
  8. end if;   
  9. ...   
  10. end;  

8. 不能在 MySQL 存储过程中使用 “return” 关键字。

 
 
  1. set c = a + b;   
  2. select c as sum;   
  3. /*   
  4. return c; -- 不能在 MySQL 存储过程中使用。return 只能出现在函数中。   
  5. */   
  6. end;  

9. 调用 MySQL 存储过程时候,需要在过程名字后面加“()”,即使没有一个参数,也需要“()”

 
 
  1. call pr_no_param();  

10. 因为 MySQL 存储过程参数没有默认值,所以在调用 MySQL 存储过程时候,不能省略参数。可以用 null 来替代。

_______________________________

存储过程学习

        文章开始前,首先给出本文示例表allIntersection。本文中的示例是在Navicate for MySQL中进行的。

 

 

 

一.存储过程的创建与调用

1.基本语法及示例:

        创建存储过程的基本语法:

 

Java代码   收藏代码
  1. create procedure sp_name()  
  2. begin  
  3.   ………  
  4. end  

        调用此存储过程基本语法:

Sql代码   收藏代码
  1. call sp_name();   

        注意:存储过程名称后面必须加括号,哪怕该存储过程没有参数传递

 

        删除存储过程的基本语法:

Sql代码   收藏代码
  1. drop procedure sp_name;  

    注意事项:不能在一个存储过程中删除另一个存储过程,只能调用另一个存储过程。

 

    其他常用命令:
        1. show procedure status
        显示数据库中所有存储的存储过程基本信息,包括所属数据库,存储过程名称,创建时间等。
        2. show create procedure sp_name
        显示某一个存储过程的详细信息。

 

    例1:

    创建一个存储过程:

Sql代码   收藏代码
  1. create procedure dayrepeat()  
  2. begin  
  3.   select IntersectionUnit as '路口单位' , IntersectionName as '路口名称' , DriveDirection as '行车方向' , SCar+MCar+BCar as '总流量'  
  4.   from allintersection;  
  5. end;  

    调用存储过程dayrepeat():

Sql代码   收藏代码
  1. call dayrepeat();  

    返回结果如下图所示:

 

 

2.带参数传递的存储过程
    例2:

Sql代码   收藏代码
  1. create procedure test1(  
  2. out ls decimal(8,2),  
  3. out hs decimal(8,2),  
  4. out avs decimal(8,2)  
  5. )  
  6. begin  
  7.   select min(SCar) as '小型车车流量最小值'  
  8.   into ls  
  9.   from allintersection;  
  10.   select max(SCar) as '小型车车流量最大值  
  11.   into hs  
  12.   from allintersection;  
  13.   select avg(SCar) as '小型车车流量平均值'  
  14.   into avs  
  15.   from allintersection;  
  16. end;  

        此存储过程接受3个参数:ls、hs、avs。每个参数必须制定类型,这里使用十进制值。关键字OUT指出相应的参数用来从存储过程传出一个值(返回给调用者)。MySQL支持IN(传递给存储过程)、OUT(从存储过程传出)和INOUT(对存储过程传入和传出)
注意:记录集不是允许的类型,因此,不能通过一个参数返回多个行和列。

        由于此存储过程要求3个参数,因此必须正好传递3个参数,不多也不少,所以这条call语句给出3个参数,他们是存储过程保存结果的3个变量的名字。

Sql代码   收藏代码
  1. call test1(@小型车车流量最小值,@小型车车流量最大值,@小型车车流量平均值);  

        在调用时,这条语句并不显示任何数据。它返回以后可以显示(或在其他处理中使用)的变量。


        为了显示检索出的结果,可如下进行:

Sql代码   收藏代码
  1. SELECT @小型车车流量平均值 , @小型车车流量最大值 , @小型车车流量平均值;  

    检索结果如下图所示:

 

 

例3:
        下面是另外一个例子,这次使用IN和OUT参数。isTotal接受路口名称参数并返回该路口的总车流量。

Sql代码   收藏代码
  1. create procedure isTotal(  
  2. IN isID int,  
  3. OUT iTotal decimal(8,2)  
  4. )  
  5. begin  
  6.   select SCar+MCar+BCar AS '路口总车流量'  
  7.   from allintersection  
  8.   where ID = isID  
  9.   into iTotal;  
  10. end;  

        isID被定义为IN,因为ID被传入存储过程。iTotal定义为OUT,因为要从存储过程返回合计。

 

        为调用这个存储过程和显示合计结果,可使用以下语句:

Sql代码   收藏代码
  1. call isTotal(1,@total);  
  2. select @total;  

        结果如下图所示:

 

 

二.使用游标

1.创建游标
        游标用declare语句创建。如下面的例子所示:

Sql代码   收藏代码
  1. create procedure test2()  
  2. begin  
  3.     declare cursorTest cursor  
  4.     for  
  5. select *  
  6. from allIntersection;  
  7. end;  

2.打开和关闭游标
        游标用OPEN CURSOR语句来打开,用CLOSE CURSOR来关闭

Sql代码   收藏代码
  1. open cursorTest;  
  2. close cursorTest;  

        CLOSE释放游标使用的所有内部内存和资源,因此在每个游标不再需要时都应该关闭。在一个游标关闭后,如果没有重新打开,则不能使用它。但是,声明过的游标不需要再次声明,用OPEN语句打开它就可以了。
3.使用游标数据
        在一个游标被打开后,可以使用FETCH语句分别访问它的每一行。FETCH语句指定检索什么数据(所需的列),检索出来的数据存储在什么地方。它还向前移动游标中的内部行指针,使下一条FETCH语句检索下一行(不重复读取同一行)。

Sql代码   收藏代码
  1. create procedure test3()  
  2. begin  
  3.     declare o int;            -- 声明一个局部变量  
  4.   declare cursorTest3 cursor        -- 声明一个游标  
  5.     for  
  6.       select ID  
  7.     from allintersection;  
  8.     open cursorTest3;          -- 打开游标  
  9.     fetch cursorTest3 into o;              -- 获取IntersectionName  
  10.     close cursorTest3;  -- 关闭游标  
  11. end;  

 

        其中FETCH用来检索当前行的IntersectionName列(将自动从第一行开始)到一个名为o的局部声明的变量中。对检索出的数据部做任何处理。

 

例4:

        这个例子循环检索数据,从第一行到最后一行。

Sql代码   收藏代码
  1. create procedure test4()  
  2. begin  
  3.     declare done boolean default 0;  
  4.     declare o int;            -- 声明一个局部变量  
  5.   declare cursorTest4 cursor        -- 声明一个游标  
  6.     for  
  7.       select ID  
  8.     from allintersection;  
  9.   declare continue handler for sqlstate '02000' set done=1;  
  10.     open cursorTest4;          -- 打开游标  
  11.     -- 遍历所有的行  
  12.     repeat  
  13.            fetch cursorTest4 into o;              -- 获取IntersectionName  
  14.     until done end repeat;           -- 结束循环  
  15.     close cursorTest4;  -- 关闭游标  
  16. end;  

        与上一个例子不同的是,这个例子中的FETCH是在REPEAT内,因此它反复执行到done为真(until done end repeat;规定)。为使它起作用,用一个default 0(假,不结束)定义变量done。那么,done怎样才能在结束时被设置为真呢?答案是用以下语句:

Sql代码   收藏代码
  1. declare continue handler for sqlstate '02000' set done=1;  

        这条语句定义了一个CONTINUE HANDLER,它是在条件出现时被执行的代码。这里,它指出SQLSTATE '02000'出现时,set done=1。SQLSTATE '02000'是一个未找到条件,当REPEAT由于没有更多的行供循环而不能继续时,出现这个条件。

 

 

例5:

        这个例子比上面的例子更复杂,对取出的数据进行了实际的处理。

Sql代码   收藏代码
  1. create procedure test5()  
  2. begin  
  3.     -- 声明局部变量  
  4.     declare done boolean default 0;  
  5.     declare o int;             
  6.   
  7.   -- declare t DECIMAL(8,2);  
  8.   
  9.   declare cursorTest5 cursor        -- 声明一个游标  
  10.   
  11.     for  
  12.       select ID from allintersection;  
  13.   -- 定义continue handler  
  14.   declare continue handler for sqlstate '02000' set done=1;             
  15.     -- 创建daysRepeat表来存储结果  
  16.     create table if not exists daysRepeat  
  17.     (ID int , total decimal(8,2));  
  18.   
  19.     open cursorTest5;          -- 打开游标  
  20.      
  21.   -- 遍历所有的行  
  22. fetch cursorTest5 into o;    
  23. repeat  
  24.            call isTotal(o , @t);  
  25.            insert into daysRepeat(ID , total)  
  26.            values(o,@t);  
  27.            fetch cursorTest5 into o;              -- 获取IntersectionName  
  28.     until done end repeat;           -- 结束循环  
  29.     close cursorTest5;  -- 关闭游标  
  30. end;  

        然后执行存储过程:

Sql代码   收藏代码
  1. call test5();    

        结果生成新的表daysRepeat,并往表里填充数据,填充后的表如下图所示:

猜你喜欢

转载自l62s.iteye.com/blog/1532745