mysql全家桶(四)存储过程

一、存储过程

1、介绍

简单的说,就是一组SQL语句集,功能强大,可以实现一些比较复杂的逻辑功能,类似于JAVA语言中的方法;

存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。

存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。

存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

2、优缺点

优点:

  • 存储过程可封装,并隐藏复杂的商业逻辑。
  • 存储过程可以回传值,并可以接受参数。
  • 存储过程无法使用 SELECT 指令来运行,因为它是子程序,与查看表,数据表或用户定义函数不同。
  • 存储过程可以用在数据检验,强制实行商业逻辑等。
  • 在生产环境下,可以通过直接修改存储过程的方式修改业务逻辑(或bug),而不用重启服务器(人直接就在正式服务器上修改存储过程,而没有经过完整的测试,后果非常严重。)

  • 执行速度快。存储过程经过编译之后会比单独一条一条执行要快。但这个效率真是没太大影响。如果是要做大数据量的导入、同步,我们可以用其它手段。

  • 减少网络传输。存储过程直接就在数据库服务器上跑,所有的数据访问都在服务器内部进行,不需要传输数据到其它终端。但我们的应付服务器通常与数据库是在同一内网,大数据的访问的瓶颈会是硬盘的速度,而不是网速。

  • 能够解决presentation(把属性映射到字段)与数据之间的差异(解决OO模型与二维数据持久化之间的阻抗)

缺点:

  • 存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。
  • 存储过程的性能调校与撰写,受限于各种数据库系统。
  • 不便于调试。基本上没有较好的调试器

  • 没办法应用缓存。虽然有全局临时表之类的方法可以做缓存

  • 无法适应数据库的切割(水平或垂直切割)。数据库切割之后,存储过程并不清楚数据存储在哪个数据库中。

3、创建

*创建

#创建
CREATE PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_body

CREATE PROCEDURE  过程名([[IN|OUT|INOUT] 参数名 数据类型[,[IN|OUT|INOUT] 参数名 数据类型…]]) [特性 ...] 过程体

其中,sp_name参数是存储过程的名称;proc_parameter表示存储过程的参数列表; characteristic参数指定存储过程的特性;routine_body参数是SQL代码的内容,可以用BEGIN…END来标志SQL代码的开始和结束。

*分隔符

MySQL默认以";"为分隔符,如果没有声明分割符,则编译器会把存储过程当成SQL语句进行处理,因此编译过程会报错,所以要事先用“DELIMITER //”声明当前段分隔符,让编译器把两个"//"之间的内容当做存储过程的代码,不会执行这些代码;“DELIMITER ;”的意为把分隔符还原。

DELIMITER $$ #将语句的结束符号从分号;临时改为两个$$(可以是自定义)
存储过程语句
$$
DELIMITER;

或

DELIMITER //
存储过程语句
//
DELIMITER ;

*参数(参数类型、参数名、数据类型)

存储过程根据需要可能会有输入、输出、输入输出参数,如果有多个参数用","分割开。

a.参数类型

MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT:

  • IN输入参数,不被输出
  • OUT:不用输入,直接输出
  • INOUT:输入,执行后输出

b.参数名

可以自定义

c.数据类型(见mysql全家桶(一)数据库以及表操作的字段类型

 

mysql全家桶(四)存储过程_第1张图片

d.示例

#示例

DELIMITER //
CREATE PROCEDURE num_from_employee (IN emp_id INT, OUT count_num INT ) 
READS SQL DATA 
     BEGIN 
       SELECT COUNT(*) INTO count_num 
       FROM employee 
       WHERE d_id=emp_id ; 
     END
//
DELIMITER;

创建 num_from_employee存储过程,传入整型emp_id,返回整型count_num

*characteristict存储过程的特性

参数有多个取值。其取值说明如下:

  • LANGUAGE SQL:说明routine_body部分是由SQL语言的语句组成,这也是数据库系统默认的语言。
  • [NOT] DETERMINISTIC:指明存储过程的执行结果是否是确定的。DETERMINISTIC表示结果是确定的。每次执行存储过程时,相同的输入会得到相同的输出。NOT 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':注释信息。

*routine_body过程体

routine_body参数是SQL代码的内容,可以用BEGIN…END来标志SQL代码的开始和结束。

  • 存储过程体包含了在过程调用时必须执行的语句,例如:dml(数据操纵语言、INSERT、UPDATE、DELETE)、ddl语句(数据库模式定义语言create、alter),if-then-else和while-do语句、声明变量的declare语句等
  • 过程体格式:以begin开始,以end结束(可嵌套)
BEGIN
  BEGIN
    BEGIN
      statements; 
    END
  END
END

注意:每个嵌套块及其中的每条语句,必须以分号结束,表示过程体结束的begin-end块(又叫做复合语句compound statement),则不需要分号。

为语句块贴标签:

[begin_label:] BEGIN
  [statement_list]
END [end_label]
#例如
label1: BEGIN
  label2: BEGIN
    label3: BEGIN
      statements; 
    END label3 ;
  END label2;
END label1

标签有两个作用:

  • 1、增强代码的可读性
  • 2、在某些语句(例如:leave和iterate语句),需要用到标签

变量

局部变量声明一定要放在存储过程体的开始:

#定义
DECLARE variable_name [,variable_name...] datatype [DEFAULT value];

#例如(variable_name变量名、datatype 为 MySQL 的数据类型,如: int, float, date,varchar(length))

DECLARE l_int int unsigned default 4000000;  
DECLARE l_numeric number(8,2) DEFAULT 9.95;  
DECLARE l_date date DEFAULT '1999-12-31';  
DECLARE l_datetime datetime DEFAULT '1999-12-31 23:59:59';  
DECLARE l_varchar varchar(255) DEFAULT 'This will not be padded';
#变量赋值
SET 变量名 = 表达式值 [,variable_name = expression ...]

#例如
SET var_name = expr [, var_name = expr] ...

#MySQL中还可以使用SELECT…INTO语句为变量赋值。其基本语法如下:
SELECT col_name[,…] INTO var_name[,…] FROM table_name WEHRE condition 

#其中,col_name参数表示查询的字段名称;var_name参数是变量的名称;table_name参数指表的名称;condition参数指查询条件。

 用户变量

基于会话变量实现的,可以暂存值,并传递给同一连接里的下一条sql使用的变量,当客户端连接退出时,变量会被释放。

#在MySQL客户端使用用户变量
SELECT 'Hello World' into @x;
SELECT @x;

#输出
+-------------+  
|   @x        |  
+-------------+  
| Hello World |  
+-------------+  

#在存储过程中使用用户变量
CREATE PROCEDURE GreetWorld( ) SELECT CONCAT(@greeting,' World');  
SET @greeting='Hello';
CALL GreetWorld( );

#输出
+----------------------------+  
| CONCAT(@greeting,' World') |  
+----------------------------+  
|  Hello World               |  
+----------------------------+

#在存储过程间传递全局范围的用户变量
CREATE PROCEDURE p1()   SET @last_procedure='p1';  
CREATE PROCEDURE p2() SELECT CONCAT('Last procedure was ',@last_procedure);  
CALL p1( );  
CALL p2( );  

#输出
+-----------------------------------------------+  
| CONCAT('Last procedure was ',@last_proc       |  
+-----------------------------------------------+  
| Last procedure was p1                         |  
 +-----------------------------------------------+

定义条件

MySQL中可以使用DECLARE关键字来定义条件。其基本语法如下:

DECLARE condition_name CONDITION FOR condition_value 
condition_value: 
   SQLSTATE [VALUE] sqlstate_value | mysql_error_code 

其中,condition_name参数表示条件的名称;condition_value参数表示条件的类型;sqlstate_value参数和mysql_error_code参数都可以表示MySQL的错误。例如ERROR 1146 (42S02)中,sqlstate_value值是42S02,mysql_error_code值是1146。

#例如

//方法一:使用sqlstate_value 
DECLARE can_not_find CONDITION FOR SQLSTATE '42S02' ; 
//方法二:使用mysql_error_code 
DECLARE can_not_find CONDITION FOR 1146 ; 

定义处理程序

MySQL中可以使用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 FOUND | SQLEXCEPTION | mysql_error_code

其中,handler_type参数指明错误的处理方式,该参数有3个取值。这3个取值分别是CONTINUE、EXIT和UNDO。CONTINUE表示遇到错误不进行处理,继续向下执行;EXIT表示遇到错误后马上退出;UNDO表示遇到错误后撤回之前的操作,MySQL中暂时还不支持这种处理方式。

  注意:通常情况下,执行过程中遇到错误应该立刻停止执行下面的语句,并且撤回前面的操作。但是,MySQL中现在还不能支持UNDO操作。因此,遇到错误时最好执行EXIT操作。如果事先能够预测错误类型,并且进行相应的处理,那么可以执行CONTINUE操作。

  condition_value参数指明错误类型,该参数有6个取值。sqlstate_value和mysql_error_code与条件定义中的是同一个意思。condition_name是DECLARE定义的条件名称。SQLWARNING表示所有以01开头的sqlstate_value值。NOT FOUND表示所有以02开头的sqlstate_value值。SQLEXCEPTION表示所有没有被SQLWARNING或NOT FOUND捕获的sqlstate_value值。sp_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"信息。

注释

MySQL 存储过程可使用两种风格的注释

两个横杆--:该风格一般用于单行注释。

c 风格: 一般用于多行注释。/*注释*/

#例如
DELIMITER //  
CREATE PROCEDURE proc1 --name存储过程名  
     -> (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 ;

4、调用

用call和你过程名以及一个括号,括号里面根据需要,加入参数,参数包括输入参数、输出参数、输入输出参数。具体的调用方法可以参看上面的例子。

call product();    //无参
call procedure2(@userSum,@userAvg,201708);    //有参

5、查询

查看某个数据库下面的存储过程

showtables;

或者

selectname from mysql.proc where db='数据库名';

或者

selectroutine_name from information_schema.routines where routine_schema='数据库名';

或者

showprocedure status where db='数据库名';

查看存储过程的详细

SHOWCREATE PROCEDURE 数据库.存储过程名;

6、修改

ALTER PROCEDURE

7、删除

DROPPROCEDURE

8、MySQL存储过程的控制语句

(1). 变量作用域

内部的变量在其作用域范围内享有更高的优先权,当执行到 end。变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储过程外再也不能找到这个申明的变量,但是你可以通过 out 参数或者将其值指派给会话变量来保存其值。

DELIMITER //  
    CREATE PROCEDURE proc3()  
        begin 
            declare x1 varchar(5) default 'outer';  
            begin 
                declare x1 varchar(5) default 'inner';  
                select x1;  
            end;  
            elect x1;  
        end;  
//  
DELIMITER ;

(2). 条件语句

1. if-then-else 语句

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 ;

2. case语句:

DELIMITER //  
CREATE PROCEDURE proc3 (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 ; 

#结构
case
    when var=0 then
        insert into t values(30);
    when var>0 then
    when var<0 then
    else
end case

(3). 循环语句

1. while ···· end while

DELIMITER //  
CREATE PROCEDURE proc4()  
begin 
declare var int;  
set var=0;  
while var<6 do  
insert into t values(var);  
set var=var+1;  
end while;  
end;  
//  
DELIMITER ;
while 条件 do
    --循环体
endwhile

2. repeat···· end repea

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

DELIMITER //  
CREATE PROCEDURE proc5 ()  
begin   
declare v int;  
set v=0;  
repeat  
insert into t values(v);  
set v=v+1;  
until v>=5  
end repeat;  
end;  
//  
DELIMITER ;
repeat
    --循环体
until 循环条件  
end repeat;

3. loop ·····endloop

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

DELIMITER //  
CREATE PROCEDURE proc6 ()  
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 ;

4. LABLES 标号:

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

(4). ITERATE迭代

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 ;

 

你可能感兴趣的:(mysql)