MySQL存储过程

 简介

  存储过程就是一条或多条SQL语句的集合,当对数据库进行一系列复杂操作时,存储过程可以将这些复杂的操作封装程一个代码块,可以重复使用,大大减少数据库开发人员的工作量。

 存储过程的创建

  创建存储过程

  使用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表示结果是确定的。每次执行存储过程时,相同的输入会得到相同的输出;NOT DETERMINISTIC表示结果是不确定的,相同的输入可能得到不同的输出。如果没有指定任意一个值,默认为NOT DE-TERMINISTIC。
  • {CONTAINS SQL|NO SQL|READS SQLDATA|MODIFIES SQL DATA}:指明子程序使用SQL语句的限制。CONTAINS SQL表明子程序包含SQL语句,但是不包含读写数据的语句;NOSQL表明子程序不包含SQL语句;READS SQLDATA说明子程序包含读写数据的语句;MODI-FIES SQL DATA表明子程序包含写数据的语句。默认情况下,系统会指定为CONTAINS SQL。
  • SQL SECURITY{DEFINER|INVOKER}:指明谁有权限来执行。DEFINER表示只有定义者才能执行。INVOKER表示拥有权限的调用者可以执行。默认情况下,系统指定为DEFINER。
  • COMMENT‘string’:注释信息,可以用来描述存储过程。

  routime_bodySQL代码的内容,可以用BEGIN…END来表示SQL代码的开始和结束

  创建存储过程案例

mysql> DELIMITER //
mysql> CREATE PROCEDURE Proc ()
    -> BEGIN
    -> SELECT * FROM table_name;
    -> END //
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;

  在上述执行过程中,“DELIMITER //”语句的作用是将MySQL的结束符设置为//,因为MySQL默认的语句结束符号为分号“;”,为了避免与存储过程中SQL语句结束符相冲突,需要使用DELIMITER改变存储过程的结束符,并以“END //”结束存储过程。存储过程定义完毕后再使用“DELIMITER ;”恢复默认结束符。DELIM-ITER也可以指定其他符号作为结束符。需要格外注意的是,DELIMITER与要设定的结束符之间一定要有一个空格,否则设定无效

  变量的使用

  在编写存储过程时,有时会需要使用变量保存数据处理过程中的值。在MySQL中,变量可以在子程序中声明并使用,这些变量的作用范围是在BEGIN…END程序中

  • 在存储过程中使用DECLARE语句定义变量,具体语法格式如下:
DECLARE var_name[,varname]…date_type[DEFAULT value];

  上述语法格式中var_name:局部变量的名称;DEFAULT value子句给变量提供一个默认值。该值除了可以被声明为一个常数之外,还可以被指定为一个表达式。如果没有DEFAULT子句,变量的初始值为NULL。

  接下来定义一个名称为myvariable的变量,类型为INT类型,默认值为100,示例代码如下:

DECLARE myvariable INT DEFAULT 100;
  • 使用SET语句为变量赋值,可以改变变量的默认值,语法格式如下:
SET var_name = expr[,var_name = expr]…;

  在存储过程中的SET语句是一般SET语句的扩展版本。被参考变量可能是子程序内声明的变量,或者是全局服务器变量,如系统变量或者用户变量。

  在存储程序中的SET语句作为预先存在的SET语法的一部分来实现。这允许SET a=x,b=y,…这样的扩展语法。其中不同的变量类型(局域声明变量及全局变量)可以被混合起来。这也允许把局部变量和一些只对系统变量有意义的选项合并起来。

  接下来声明三个变量,分别为var1、var2、var3,数据类型为INT,使用SET为变量赋值,示例代码如下:

DECLARE var1,var2,var3 INT;
SET var1=10,var2=20;
SET var3=var1+var2;
SET @x=var1+var2;

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

  • 除了可以使用SET语句为变量赋值外,MySQL中还可以通过SELECT…INTO为一个或多个变量赋值,该语句可以把选定的列直接存储到对应位置的变量。使用SELECT…INTO的具体语法格式如下:
SELECT col_name[] INTO var_name[] table_expr;

  在上述语法格式中,col_name表示字段名称;var_name表示定义的变量名称;table_expr表示查询条件表达式,包括表名称和WHERE子句。

  :声明变量s_grade和s_gender,通过SELECT…INTO语句查询指定记录并为变量赋值,具体代码如下:

DECLARE s_grade FLOAT;
DECLARE s_gender CHAR(2);
SELECT grade, gender INTO s_grade, s_gender
FROM student WHERE name = 'rose';

  定义条件和处理程序

  定义条件是事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时应当采取的处理方式,并且保证存储过程在遇到警告或错误时能继续执行。

  • 定义条件
    在编写存储过程时,定义条件使用DECLARE语句,语法格式如下:
    DECLARE condition_name CONDITION FOR [condition_type];
    // condition_type的两种形式: [condition_type]:SQLSTATE[VALUE] sqlstate_value|mysql_error_code

    上述语法格式中,condition_name表示所定义的条件的名称;condition_type表示条件的类型;sqlstate_value和mysql_error_code都可以表示MySQL的错误,sqlstate_value是长度为5的字符串类型错误代码,mysql_error_code为数值类型的错误代码。例如:ERROR1142(42000)中,sql-state_value的值是42000,mysql_error_code的值是1142。
    上述语法格式指定了需要特殊处理的条件。它将一个名字和指定的错误条件关联起来。这个名字可以随后被用在定义处理程序的DECLARE HAN-DLER语句中。

    :定义“ERROR1148(42000)”错误,名称为command_not_allowed的两种方式:
    //方法一:使用sqlstate_value
    DECLARE command_not_allowed CONDITION FOR SQLSTATE'42000';
    //方法二:使用mysql_error_code
    DECLARE command_not_allowed CONDITION FOR 1148;
  • 定义处理程序
    定义完条件后,还需要定义针对此条件的处理程序。MySQL中用DECLARE语句定义处理程序,具体语法格式如下:
    DECLARE handler_type HANDLER FOR condition_value[,…] sp_statement
    //handler_type:  CONTINUE|EXIT|UNDO
    //condition_value:  |condition_name  |SQLWARNING  |NOT FOUND  |SQLEXCEPTION  |mysql_error_code
    handler_type为错误处理方式,CONTINUE表示遇到错误不处理,继续执行;EXIT表示遇到错误马上退出;UNDO表示遇到错误后撤回之前的操作,MySQL中暂时不支持这样的操作。
    sp_statement表示在遇到定义的错误时,需要执行的存储过程;
    condition_value为错误类型,取值:
    (1)SQLSTATE[VALUE] sqlstate_value包含5个字符的字符串错误值。
    (2)condition_name表示DECLARE CON-DITION定义的错误条件名称。
    (3)SQLWARNING匹配所有以01开头的SQLSTATE错误代码。
    (4)NOT FOUND匹配所有以02开头的SQLSTATE错误代码。
    (5)SQLEXCEPTION匹配所有没有被SQL-WARNING或NOT FOUND捕获的SQLSTATE错误代码。
    (6)mysql_error_code匹配数值类型错误代码。
  • :定义处理程序几种方式:
    //方法一:捕获sqlstate_value
    DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'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='ERROR';
    
    //方法四:使用SQLWARNING SQL-WARNING捕获所有以01开头的sqlstate_value值,然后执行EXIT操作,并且输出“ERROR”信息。
    DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';
    
    //方法五:使用NOT FOUND NOTFOUND捕获所有以02开头的sqlstate_value值,然后执行EXIT操作,并且输出“NO_SUCH_TA-BLE”信息。
    DECLARE EXIT HANDLER FOR NOT FOUND SET @info='NO_SUCH_TABLE';
    
    //方法六:使用SQLEXCEPTION SQLEXCEPTION捕获所有没有被SQLWARN-ING或NOT FOUND捕获的sqlstate_value值,然后执行EXIT操作,并且输出“ERROR”信息
    DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR';

  光标的使用

  在编写存储过程时,查询语句可能会返回多条记录,如果数据量非常大,则需要使用光标来逐条读取查询结果集中的记录。光标是一种用于轻松处理多行数据的机制。

  • 光标的声明
    光标必须声明在声明变量、条件之后,声明处理程序之前。MySQL中使用DECLARE关键字来声明光标,具体语法格式如下:
    DECLARE cursor_name CURSOR FOR select_statement;
    cursor_name:光标名称;
    select_statement:select语句,用于创建光标的结果集;
    :声明名为cursor_student的光标
    DECLARE cursor_student CURSOR FOR select s_name,s_gender FROM student;
  • 光标的使用
    声明完光标就可以使用了,使用前首先要大开光标;MySQL中打开和使用光标,语法格式如下:
    OPEN cursor_name
    FETCH cursor_name INTO var_name[,var_name]
    cursor_name:光标名称;
    var_name:将光标中的SELECT语句查询出来的信息存入该参数中,需要注意的是,var_name必须在声明光标之前就定义好
    :使用名称为cursor_student的光标。将查询出来的信息存入s_name和s_gender中
    FETCH cursor_student INTO s_name, s_gender;
  • 光标的关闭
    使用完光标要将光标关闭;语法格式如下:
    CLOSE cursor_name

    值得一提的是,如果没有明确地关闭光标,它会在其声明的复合语句的末尾被关闭

  流程控制的使用 

  在编写存储过程时还有一个非常重要的部分——流程控制。MySQL中的流程控制语句包括:IF语句、CASE语句、LOOP语句、WHILE语句、LEAVE语句、ITERATE语句、REPEAT语句和WHILE语句。

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

  • IF语句
    IF实现了一个基本的条件构造。用法:
    IF val IS NULL
        THEN SELECT 'val is NULL';
        ELSE SELECT 'val is not NULL';
    END IF;

    需要注意的是,MySQL中还有一个IF()函数,它不同于这里描述的IF语句。IF语句都需要使用END IF来结束,不可省略。

  • CASE语句
    CASE是另一种条件判断的语句,该语句有两种格式,第一种如下:
    CASE case_expr
        WHEN when_value THEN statement_list
        [WHEN when_value THEN statement_list][ELSE statement_list]
    END CASE

    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;

    第二种如下:

    CASE
      WHEN expr_condition THEN statement_list
      [WHEN expr_condition THEN statement_list]
      [ELSE statement_list]
    END CASE;

    需要注意的是,这里说的用在存储过程里的CASE语句与“控制流程函数”里描述的SQL CASE表达式中的CASE语句有些不同。存储过程里的CASE语句不能有ELSE NULL子句,并且用ENDCASE替代END来终止

  • LOOP语句
    LOOP循环语句是用来重复执行某些语句,直到跳出循环语句。语法格式如下:
    [loop_label:]LOOP
        statement_list
    END LOOP [loop_label]
    loop_label:指LOOP语句的标注名称,可省略。
    statement_list:需要循环执行的语句。
    :使用LOOP语句进行循环操作
    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;

    当id值小于10时,循环重复执行;当id值大于或者等于10时,使用LEAVE语句退出循环

  • LEAVE语句
    LEAVE语句用于退出任何被标注的流程控制构造,如LOOP例子中,当不满足循环条件时,需要使用LEAVE语句退出循环。基本语法如下:
    LEAVE label
    label表示循环的标志。通常LEAVE语句与BEGIN。。。END、循环语句一起使用
  • ITERATE语句
    ITERATE即再次循环,用于将执行顺序转到语句段的开头处。基本语法如下:
    ITERATE lable
    lable表示循环的标志。需要注意的是,ITERATE 语句只能出现在LOOP、REPEAT和WHILE语句内
    :演示了ITERATE语句在LOOP语句内的使用
    CREATE PROCEDURE doiterate()
    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';
    END LOOP my_loop;
    END
  • REPEAT语句
    REPEAT语句用于创建一个带有条件判断的循环过程,每次执行后,会对条件进行判断,如果为真,则循环结束;否则重复执行循环中的语句。基本语法如下:
    [repeat_lable:] REPEAT
       statement_list
    UNTIL expr_condition
    END REPEAT[repeat_lable]
    repeat_lable指REPEAT语句的标注名称(可选);REPEAT语句内的语句或语句群被重复,直至expr_condition为真。
    :演示使用REPEAT语句执行循环过程
    DECLARE id INT DEFAULT 0;
    REPEAT
    SET id=id+1;
    UNTIL id>=10;
    END REPEAT;
  • WHILE语句
    WHILE语句用于创建一个带有条件判断的循环过程,与REPEAT不同的是在语句执行时,先对指定的表达式进行判断,如果为真,则执行循环内的语句,否则退出循环。基本语法如下:
    [while_lable:] WHILE expr_condition DO
         Statement_list
    END WHILE [while_lable]
    while_lable指WHILE语句的标注名称;
    expr_condition为进行判断的表达式,如果为真,WHILE语句内的语句或语句群被执行,直至expr_condition为假,退出循环。
    :示使用WHILE语句进行循环操作
    DECLARE i INT DEFAULT 0;
    WHILE i<10 DO
    SET i=i+1;
    END WHILE;

 存储过程的使用

  调用存储过程

  存储过程必须用CALL语句调用,如果调用其他数据库的存储过程,需要指定数据库名。调用语法格式如下:

CALL sp_name([parameter[,…]])

  其中,sp_name:存储过程的名称;parameter:存储过程的参数;

  例:定义一个名为CountProc1的存储过程,然后调用这个存储过程,具体操作如下:

  1. 定义存储过程:
    mysql> DELIMITER //
    mysql> CREATE PROCEDURE CountProc1(IN s_gender VARCHAR(50),OUT num INT)
        -> BEGIN
        -> SELECT COUNT(*) INTO num FROM student WHERE gender= s_gender;
        -> END//
    Query OK, 0 rows affected (0.13 sec)
    mysql> DELIMITER;
  2. 调用存储过程:
    mysql> CALL CountProc1("女",@num);
    Query OK, 1 row affected (0.17 sec)
  3. 查看返回结果
    mysql> SELECT @num;
    +------+
    | @num |
    +------+
    |  2   |
    +------+
    1 row in set (0.00 sec)

  查看存储过程

  1. SHOW STATUS语句
    基本语法结构:
    SHOW {PROCEDURE|FUNCTION} STATUS [LIKE 'pattern'\]

    上述语法格式中,PROCEDURE和FUNCTION分别表示查看存储过程和函数,LIKE语句表示匹配的名称。

    示例:获取数据库中所有名称以C开头的存储过程的信息。
    SHOW PROCEDURE STATUS LIKE'C%'\G
  2. SHOW CREATE语句
    基本语法结构:
    SHOW CREATE{PROCEDURE|FUNCTION} sp_name

    示例:

    SHOW CREATE PROCEDURE chapter06.CountProc1\G
  3. 从information_schema.Routines表中查看存储过程的信息
    在MySQL中存储过程和函数的信息存储在information_schema数据库下的Routines表中。可以通过查询该表的记录来查询存储过程的信息,查询语句如下:
    SELECT * FROM  information_schema.RoutinesWHERE ROUTINE_NAME='CountProc1' AND ROUTINE_TYPE='PROCEDURE'\G

    需要注意的是,ROUTINE_NAME:指定存储过程的名称;ROUTINE_TYPE:指定存储程序的类型。

  修改存储过程

  使用ALTER语句修改存储过程的特性,其基本语法格式如下:

ALTER {PROCEDURE|FUNCTION} sp_name[characteristic…]

  上述语法格式中,sp_name:存储过程或函数的名称;characteristic表示要修改存储过程的哪个部分,characteristic的取值具体如下:

  1. CONTAINS SQL表示子程序包含SQL语句,但不包含读或写数据的语句;
  2. NO SQL表示子程序中不包含SQL语句;
  3. READS SQL DATA表示子程序中包含读数据的语句;
  4. MODIFIES SQL DATA表示子程序中包含写数据的语句;
  5. SQL SECURITY{DEFINER|INVOKER}指明谁有权限来执行;(DEFINER:只有定义者自己才能够执行;INVOKER:调用者可以执行)
  6. COMMENT‘string’表示注释信息。

  目前,MySQL还不提供对已存在的存储过程代码的修改,如果一定要修改存储过程代码,必须先将存储过程删除之后,再重新编写代码,或创建一个新的存储过程。

  删除存储过程

  使用DROP语句删除存储过程,其基本语法格式如下:

DROP{ PROCEDURE|FUNCTION }[IF EXISTS] sp_name

  示例:删除存储过程CountProc1

DROP PROCEDURE CountProc1;

  综合案例-存储过程应用

  1. 创建一个stu表
    表结构:

    表数据:

    建表及初始化数据脚本:
    CREATE TABLE stu(id INT,name VARCHAR(50),class VARCHAR(50));
    INSERT INTO stu VALUE (1,'Lucy','class1'),(2,'Tom','class1'),(3,'Rose','class2');
  2. 创建一个存储过程
    创建一个存储过程addcount能够获取表stu中的记录数和id的和,代码格式如下:
    CREATE PROCEDURE addcount(out count INT)
    BEGIN
    DECLARE itmp INT;
    DECLARE cur_id CURSOR FOR SELECT id FROM stu;
    DECLARE EXIT HANDLER FOR NOT FOUND CLOSE cur_id;
    SELECT count(*) INTO count FROM stu;
    SET @sum=0;
    OPEN cur_id;
    REPEAT
    FETCH cur_id INTO itmp;
    IF itmp<10
    THEN SET @sum=@sum+itmp;
    END IF;
    UNTIL 0 END REPEAT;
    CLOSE cur_id;
    END;

    上述存储过程用到了变量的声明、光标、流程控制。SQL语句执行情况如下:

    mysql> DELIMITER //
    mysql> CREATE PROCEDURE addcount(out count INT)
        -> BEGIN
        -> DECLARE itmp INT;
        -> DECLARE cur_id CURSOR FOR SELECT id FROM stu;
        -> DECLARE EXIT HANDLER FOR NOT FOUND CLOSE cur_id;
        -> SELECT count(*) INTO count FROM stu;
        -> SET @sum=0;
        -> OPEN cur_id;
        -> REPEAT
        -> FETCH cur_id INTO itmp;
        -> IF itmp<10
        -> THEN SET @sum=@sum+itmp;
        -> END IF;
        -> UNTIL 0 END REPEAT;
        -> CLOSE cur_id;
        -> END //
    Query OK, 0 rows affected (0.00 sec)
    mysql> CALL addcount(@count) //
    Query OK, 0 rows affected (0.00 sec)
    mysql> SELECT @count,@sum //
    +--------+------+
    | @count | @sum |
    +--------+------+
    |    3   |    6 |
    +--------+------+
    1 row in set (0.00 sec)
    mysql> DELIMITER;

    从调用存储过程的结果可以看出,stu表中共有三条数据,id之和为6。这个存储过程创建了一个cur_id的光标,使用这个光标来获取每条记录的id,使用REPEAT循环语句来实现所有id号相加。

posted @ 2017-06-07 11:11  灰太郎^_^  阅读(756)  评论(1编辑  收藏  举报