zoukankan      html  css  js  c++  java
  • 014:存储过程和函数

    一. 存储过程和函数

    1.存储过程和函数介绍

    • 存储过程函数是在数据库中定义一些SQL语句的集合,然后直接调用这些存储过程和函数来执行已经定义好的SQL语句;
    • 用户可以通过存储过程名传参多次调用的程序模块;
    • 存储过程和函数的特点:
      • 使用灵活,可以使用流控语句、自定义变量等完成复杂的业务逻辑
      • 提高数据安全性,屏蔽应用程序直接对表的操作,易于进行审计;
      • 减少网络传输;
      • 提高代码维护的复杂度,实际使用需要结合业务评估;
    CREATE
        [DEFINER = { user | CURRENT_USER }]      
        PROCEDURE sp_name ([proc_parameter[,...]])
        [characteristic ...] routine_body
    -
    --sp_name参数是存储过程的名称;
    --proc_parameter表示存储过程的参数列表; 
    --characteristic参数指定存储过程的特性;
    --routine_body参数是SQL代码的内容,可以用BEGIN…END来标志SQL代码的开始和结束。
    -
    
    CREATE
        [DEFINER = { user | CURRENT_USER }]
        FUNCTION sp_name ([func_parameter[,...]])
        RETURNS type
        [characteristic ...] routine_body
    
    -
    --sp_name参数是存储函数的名称;
    --func_parameter表示存储函数的参数列表; 
    --characteristic参数指定存储函数的特性;
    --routine_body参数是SQL代码的内容,可以用BEGIN…END来标志SQL代码的开始和结束。
    -
    
    proc_parameter:
        [ IN | OUT | INOUT ] param_name type 
    -     
    --IN表示输入参数;OUT表示输出参数; INOUT表示既可以是输入,也可以是输出; param_name参数是存储过程的参数名称;type参数指定存储过程的参数类型,该类型可以是MySQL数据库的任意数据类型。
    --注意:只有procedure才有in(传入),out(传出),inout(传入传出)参数,自定义函数(只有)默认就是 in
    -
    
    func_parameter:
        param_name type
    
    type:
        Any valid MySQL data type
    
    characteristic:                         
        COMMENT 'string'                                                      --COMMENT‘string’  注释信息
        | LANGUAGE SQL                                                        
        | [NOT] DETERMINISTIC                                                 
        | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }      
        | SQL SECURITY { DEFINER | INVOKER }
    -
    --LANGUAGE SQL:说明routine_body部分是由SQL语言的语句组成,这也是数据库系统默认的语言。
    --[NOT] DETERMINISTIC:指明存储过程的执行结果是否是确定的。DETERMINISTIC表示结果是确定的。每次执行存储过程时,相同的输入会得到相同的输出。NOT DETERMINISTIC表示结果是非确定的,相同的输入可能得到不同的输出。默认情况下,结果是非确定的
    -- CONSTAINS SQL           子程序包含SQL,但不包含读写数据的语句,默认
    -- NO SQL                  子程序中不包含SQL语句
    -- READS SQL DATA          子程序中包含读数据的语句
    -- MODIFIES SQL DATA       子程序中包含了写数据的语句
    -- SQLSECURITY {DEFINER|INVOKER},指明谁有权限执行。
    --   DEFINER 只有定义者自己才能够执行,默认
    --   INVOKER 表示调用者可以执行
    -- 技巧:创建存储过程时,系统默认指定CONTAINS SQL,表示存储过程中使用了SQL语句。但是,如果存储过程中没有使用SQL语句,最好设置为NO SQL。而且,存储过程中最好在COMMENT部分对存储过程进行简单的注释,以便以后在阅读存储过程的代码时更加方便
    -
    
    routine_body:
        Valid SQL routine statement
    

    2.创建存储过程和函数

    2.1 创建存储过程

    gcdb@gczheng 22:45:  [employees]> delimiter //
    gcdb@gczheng 22:47:  [employees]> CREATE  PROCEDURE  proc_from_employees (IN emp_id INT, OUT count_num INT )  
        ->           READS SQL DATA  
        ->           BEGIN  
        ->               SELECT  COUNT(*)  INTO  count_num  
        ->               FROM  employees 
        ->               WHERE emp_no=emp_id ;  
        ->           END ;//
    Query OK, 0 rows affected (0.00 sec)
    
    gcdb@gczheng 22:47:  [employees]> delimiter ;
    
    gcdb@gczheng 22:47:  [employees]> call proc_from_employees(10010,@num);  --调用存储过程
    Query OK, 1 row affected (0.00 sec)
    
    gcdb@gczheng 22:47:  [employees]> select @num;
    +------+
    | @num |
    +------+
    |    1 |
    +------+
    1 row in set (0.00 sec)
    

    上述代码中,存储过程名称为proc_from_employees;输入变量为emp_id;输出变量为count_num。SELECT语句从employee表查询emp_no值等于emp_id的记录,并用COUNT(*)计算emp_no值相同的记录的条数,最后将计算结果存入count_num中。

    说明:MySQL中默认的语句结束符为分号(;)。存储过程中的SQL语句需要分号来结束。为了避免冲突,首先用"DELIMITER //"将MySQL的结束符设置为//。最后再用"DELIMITER ;"来将结束符恢复成分号。这与创建触发器时是一样的。

    2.2 创建存储函数

    root@gczheng 23:27:  [employees]> delimiter //        
    root@gczheng 23:27:  [employees]> CREATE  FUNCTION  func_from_employees (emp_id INT )  
        ->           RETURNS VARCHAR(50)  
        ->           BEGIN  
        ->               RETURN ( SELECT concat(last_name," ",first_name)  as name
        ->               FROM  employees  
        ->               WHERE emp_no=emp_id );  
        ->           END ;//
    ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
    root@gczheng 23:27:  [employees]> delimiter ;
    
    • 上面error是由于开启了bin-log, 它控制是否可以信任存储函数创建者,不会创建写入二进制日志引起不安全事件的存储函数。明确指明函数的类型:
      • 1 DETERMINISTIC 不确定的
      • 2 NO SQL 没有SQl语句,当然也不会修改数据
      • 3 READS SQL DATA 只是读取数据,当然也不会修改数据
      • 4 MODIFIES SQL DATA 要修改数据
      • 5 CONTAINS SQL 包含了SQL语句
      • 其中在function里面,只有 DETERMINISTIC, NO SQL 和 READS SQL DATA 被支持。如果我们开启了 bin-log, 我们就必须为我们的function指定一个参数。
    
    root@gczheng 23:34:  [employees]> delimiter //        
    root@gczheng 23:34:  [employees]> CREATE  FUNCTION  func_from_employees (emp_id INT )  
        ->           RETURNS VARCHAR(50)  
        ->           READS SQL DATA  --指定了数据是读类型
        ->           BEGIN  
        ->               RETURN ( SELECT concat(last_name," ",first_name)  as name
        ->               FROM  employees  
        ->               WHERE emp_no=emp_id );  
        ->           END ;//
    Query OK, 0 rows affected (0.00 sec)
    
    root@gczheng 23:34:  [employees]> delimiter ;
    
    root@gczheng 23:52:  [employees]> select  func_from_employees(20000);  --调用函数
    +----------------------------+
    | func_from_employees(20000) |
    +----------------------------+
    | Matzke Jenwei              |
    +----------------------------+
    1 row in set (0.00 sec)
    
    

    上述代码中,存储函数的名称为func_from_employees;该函数的参数为emp_id;返回值是VARCHAR类型;SELECT语句从employee表查询emp_no值等于emp_id的记录,并将该记录的name字段的值返回

    3. 变量的使用

    在存储过程和函数中,可以定义和使用变量。用户可以使用DECLARE关键字来定义变量。然后可以为变量赋值。这些变量的作用范围是BEGIN…END程序段中

    3.1 定义变量

    --定义变量的基本语法如下:
    DECLARE  var_name[,...]  type  [DEFAULT value] 
    

    DECLARE关键字是用来声明变量的;var_name参数是变量的名称,这里可以同时定义多个变量type参数用来指定变量的类型;DEFAULT value子句将变量默认值设置为value,没有使用DEFAULT子句时,默认值为NULL

    --下面定义变量myid,数据类型为INT型,默认值为10。
    
    DECLARE  myid  INT  DEFAULT 10 ; 
    

    3.2 为变量赋值

    • 赋值方法一
    MySQL中可以使用SET关键字来为变量赋值。SET语句的基本语法如下:
    SET  var_name = expr [, var_name = expr] ... 
    

    SET关键字是用来为变量赋值的;var_name参数是变量的名称;expr参数是赋值表达式。一个SET语句可以同时为多个变量赋值,各个变量的赋值语句之间用逗号隔开。

    --下面为变量my_age赋值为30。
    
    SET  my_age = 30 ; 
    
    • 赋值方法二
    MySQL中还可以使用SELECT…INTO语句为变量赋值。其基本语法如下:
    SELECT  col_name[,…]  INTO  var_name[,…]  
        FROM  table_name  WEHRE  condition 
    

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

    下面从dept_emp表中查询emp_no为10020的记录,将该记录的dept_no值赋给变量dept_id。
    
    SELECT dept_no INTO dept_id FROM dept_emp WHERE emp_no=10020;
    

    4.定义条件和处理程序

    定义条件和处理程序是事先定义程序执行过程中可能遇到的问题。并且可以在处理程序中定义解决这些问题的办法。这种方式可以提前预测可能出现的问题,并提出解决办法。这样可以增强程序处理问题的能力,避免程序异常停止。MySQL中都是通过DECLARE关键字来定义条件和处理程序。

    4.1 定义条件

    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。

    下面定义"ERROR 1146 (42S02)"这个错误,名称为can_not_find。可以用两种不同的方法来定义,代码如下:
    //方法一:使用sqlstate_value  
    DECLARE  can_not_find  CONDITION  FOR  SQLSTATE  '42S02' ;  
    //方法二:使用mysql_error_code  
    DECLARE  can_not_find  CONDITION  FOR  1146 ; 
    

    4.2 定义处理程序

    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个取值分别是CONTINUEEXITUNDOCONTINUE表示遇到错误不进行处理,继续向下执行;EXIT表示遇到错误后马上退出;UNDO表示遇到错误后撤回之前的操作,MySQL中暂时还不支持这种处理方式。

    • 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"信息。

    5.游标的使用

    查询语句可能查询出多条记录,在存储过程和函数中使用游标来逐条读取查询结果集中的记录。游标的使用包括声明游标打开游标使用游标关闭游标。游标必须声明在处理程序之前,并且声明在变量和条件之后。

    5.1 声明游标

    MySQL中使用DECLARE关键字来声明游标。其语法的基本形式如下:
    DECLARE cursor_name CURSOR FOR select_statement ; 
    

    其中,cursor_name参数表示游标的名称;select_statement参数表示SELECT语句的内容。

    下面声明一个名为cur_employee的游标。
    
    DECLARE cur_employee CURSOR FOR SELECT emp_no,birth_date FROM employees ;
    

    上面的示例中,游标的名称为cur_employee;SELECT语句部分是从employee表中查询出emp_nobirth_date字段的值。

    5.2 打开游标

    MySQL中使用OPEN关键字来打开游标。其语法的基本形式如下:
    OPEN  cursor_name ; 
    
    --其中,cursor_name参数表示游标的名称。
    --下面打开一个名为cur_employee的游标:
    
    OPEN  cur_employee ; 
    

    5.3 使用游标

    MySQL中使用FETCH关键字来使用游标。其语法的基本形式如下:
    FETCH cur_employee INTO var_name[,var_name…] ; 
    

    其中,cursor_name参数表示游标的名称;var_name参数表示将游标中的SELECT语句查询出来的信息存入该参数中。var_name必须在声明游标之前就定义好。

    下面使用一个名为cur_employee的游标。将查询出来的数据存入row_emp_no和row_birth_date这两个变量中,代码如下:
    FETCH cur_employee INTO row_emp_no,row_birth_date;
    

    将游标cur_employee中SELECT语句查询出来的信息存入row_emp_no和row_birth_date中。row_emp_no和row_birth_date必须在前面已经定义。

    5.4 关闭游标

    MySQL中使用CLOSE关键字来关闭游标。其语法的基本形式如下:
    CLOSE  cursor_name ; 
    
    --cursor_name参数表示游标的名称。
    下面关闭一个名为cur_employee的游标:
    CLOSE  cur_employee ;
    

    关闭了这个名称为cur_employee的游标。关闭之后就不能使用FETCH来使用游标了。

    技巧:如果存储过程或函数中执行SELECT语句,并且SELECT语句会查询出多条记录。这种情况最好使用游标来逐条读取记录。游标必须在处理程序之前且在变量和条件之后声明。而且,游标使用完后一定要关闭。

    --定义游标,employees表`emp_no`和`birth_date`字段取十行
    
    root@gczheng 13:37:  [employees]> DELIMITER //        
    root@gczheng 13:39:  [employees]> CREATE PROCEDURE proc_cursor()
        -> BEGIN
        -> DECLARE row_emp_no INT;
        -> DECLARE row_birth_date DATE;
        -> DECLARE rownum INT DEFAULT 10; -- 定义取10行
        -> DECLARE i INT DEFAULT 0;
        -> DECLARE cur_employee CURSOR FOR SELECT emp_no,birth_date FROM employees ;
        -> OPEN cur_employee;
        -> REPEAT
        -> SET i:=i+1;
        -> FETCH cur_employee INTO row_emp_no,row_birth_date;
        -> SELECT row_emp_no, row_birth_date;
        -> UNTIL i>=rownum  END REPEAT;
        -> CLOSE cur_employee;
        -> END ;//
    Query OK, 0 rows affected (0.00 sec)
    
    root@gczheng 13:39:  [employees]> DELIMITER ;
    root@gczheng 13:39:  [employees]> CALL proc_cursor();
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10001 | 1953-09-02     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10002 | 1964-06-02     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10003 | 1959-12-03     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10004 | 1954-05-01     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10005 | 1955-01-21     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10006 | 1953-04-20     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10007 | 1957-05-23     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10008 | 1958-02-19     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10009 | 1952-04-19     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    +------------+----------------+
    | row_emp_no | row_birth_date |
    +------------+----------------+
    |      10010 | 1963-06-01     |
    +------------+----------------+
    1 row in set (0.13 sec)
    
    Query OK, 0 rows affected (0.13 sec)
    
    root@gczheng 13:39:  [employees]> DROP PROCEDURE proc_cursor;
    Query OK, 0 rows affected (0.01 sec)
    

    游标案例参考

    6. 流程控制语句

    流程控制语句 官方文档

    6.1 IF语句

    IF语句用来进行条件判断。根据是否满足条件,将执行不同的语句

    
    -- 语法
    IF search_condition THEN statement_list
        [ELSEIF search_condition THEN statement_list] ...
        [ELSE statement_list]
    END IF
    --
    -- 例子
    --
    gcdb@gczheng 23:04:  [employees]> DELIMITER //
    gcdb@gczheng 23:04:  [employees]> CREATE PROCEDURE proc_test1 (IN emp_id INT,OUT countnum INT)
        -> BEGIN
        -> IF emp_id > 20000 THEN 
        -> SELECT COUNT(*) INTO countnum  FROM  employees  WHERE emp_no > emp_id ; 
        -> ELSEIF emp_id = 20000 THEN 
        -> SELECT COUNT(*) INTO countnum  FROM  employees  WHERE emp_no = 20000 ;
        -> ELSE 
        -> SELECT COUNT(*) INTO countnum  FROM  employees  WHERE emp_no < emp_id ; 
        -> END IF;
        -> END //
    Query OK, 0 rows affected (0.00 sec)
    
    gcdb@gczheng 23:04:  [employees]> DELIMITER ;
    gcdb@gczheng 23:04:  [employees]> call proc_test1(20000,@countnum);
    Query OK, 1 row affected (0.01 sec)
    
    gcdb@gczheng 23:05:  [employees]> SELECT @countnum;
    +-----------+
    | @countnum |
    +-----------+
    |         1 |
    +-----------+
    1 row in set (0.00 sec)
    
    gcdb@gczheng 23:05:  [employees]> call proc_test1(20050,@countnum);
    Query OK, 1 row affected (0.06 sec)
    
    gcdb@gczheng 23:05:  [employees]> SELECT @countnum;
    +-----------+
    | @countnum |
    +-----------+
    |    289974 |
    +-----------+
    1 row in set (0.00 sec)
    
    gcdb@gczheng 23:05:  [employees]> call proc_test1(10050,@countnum);
    Query OK, 1 row affected (0.00 sec)
    
    gcdb@gczheng 23:05:  [employees]> SELECT @countnum;
    +-----------+
    | @countnum |
    +-----------+
    |        49 |
    +-----------+
    1 row in set (0.00 sec)
    
    gcdb@gczheng 23:05:  [employees]> 
    

    6.2 CASE WHEN 语法

    CASE语句也用来进行条件判断,其可以实现比IF语句更复杂的条件判断

    CASE case_value
        WHEN when_value THEN statement_list
        [WHEN when_value THEN statement_list] ...
        [ELSE statement_list]
    END CASE
    -- 或者是
    CASE
        WHEN search_condition THEN statement_list
        [WHEN search_condition THEN statement_list] ...
        [ELSE statement_list]
    END CASE
    --
    -- CASE WHEN 例子
    --
    gcdb@gczheng 23:23:  [employees]> DELIMITER //
    gcdb@gczheng 23:23:  [employees]> CREATE PROCEDURE proc_case (IN id INT)
        -> BEGIN
        -> CASE id
        ->   WHEN 1 THEN
        ->  SELECT * FROM departments WHERE dept_no='d001';
        ->   WHEN 2 THEN
        ->  SELECT * FROM departments WHERE dept_no='d002';
        ->   WHEN 3 THEN
        ->  SELECT * FROM departments WHERE dept_no='d003';
        -> ELSE 
        ->  SELECT * FROM departments WHERE dept_no='d004';
        -> END  CASE;
        -> END //
    Query OK, 0 rows affected (0.01 sec)
    
    gcdb@gczheng 23:23:  [employees]> DELIMITER ;
    gcdb@gczheng 23:23:  [employees]> call proc_case(1);
    +---------+-----------+
    | dept_no | dept_name |
    +---------+-----------+
    | d001    | Marketing |
    +---------+-----------+
    1 row in set (0.00 sec)
    
    Query OK, 0 rows affected (0.00 sec)
    
    gcdb@gczheng 23:23:  [employees]> call proc_case(3);
    +---------+-----------------+
    | dept_no | dept_name       |
    +---------+-----------------+
    | d003    | Human Resources |
    +---------+-----------------+
    1 row in set (0.00 sec)
    
    Query OK, 0 rows affected (0.00 sec)
    
    gcdb@gczheng 23:23:  [employees]> call proc_case(4);
    +---------+------------+
    | dept_no | dept_name  |
    +---------+------------+
    | d004    | Production |
    +---------+------------+
    1 row in set (0.00 sec)
    
    Query OK, 0 rows affected (0.00 sec)
    
    gcdb@gczheng 23:23:  [employees]> call proc_case(5);
    +---------+------------+
    | dept_no | dept_name  |
    +---------+------------+
    | d004    | Production |
    +---------+------------+
    1 row in set (0.00 sec)
    
    Query OK, 0 rows affected (0.00 sec)
    
    gcdb@gczheng 23:23:  [employees]> 
    

    6.3 WHILE 循环

    WHILE语句也是有条件控制的循环语句。但WHILE语句和REPEAT语句是不一样的。WHILE语句是当满足条件时,执行循环内的语句。

    -- WHILE 语法
    [begin_label:] WHILE search_condition DO
        statement_list
    END WHILE [end_label]
    
    -- WHILE举例
    (gcdb@localhost) 09:53:00 [employees]> DELIMITER //
    (gcdb@localhost) 09:53:01 [employees]> CREATE PROCEDURE proc_while (IN p_num INT, OUT p_result INT)
        -> BEGIN
        -> SET p_result = 1 ;
        -> WHILE p_num > 1 DO
        -> SET p_result = p_result * p_num ;
        -> SET p_num = p_num - 1 ;
        -> END
        -> WHILE ;
        -> END ;//
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 09:53:33 [employees]> CALL proc_while(5,@p_result);
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 09:54:09 [employees]> SELECT @p_result;
    +-----------+
    | @p_result |
    +-----------+
    |       120 |
    +-----------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 09:54:34 [employees]> CALL proc_while(1,@p_result);
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 09:54:39 [employees]> SELECT @p_result;
    +-----------+
    | @p_result |
    +-----------+
    |         1 |
    +-----------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 09:54:40 [employees]> CALL proc_while(0,@p_result);
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 09:54:44 [employees]> SELECT @p_result;
    +-----------+
    | @p_result |
    +-----------+
    |         1 |
    +-----------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 09:54:45 [employees]>
    
    

    6.4 REPEAT循环

    REPEAT语句是有条件控制的循环语句。当满足特定条件时,就会跳出循环语句。

    -- REPEAT 语法
    [begin_label:] REPEAT
        statement_list
    UNTIL search_condition
    END REPEAT [end_label]
    
    (gcdb@localhost) 09:36:12 [employees]> DELIMITER //
    (gcdb@localhost) 09:36:14 [employees]> CREATE PROCEDURE proc_repeat (IN p_num INT, OUT p_result INT)
        -> BEGIN
        -> SET p_result = 1;
        -> REPEAT
        -> SET p_result = p_result * p_num;
        -> SET p_num = p_num - 1 ;
        -> UNTIL p_num <= 1 END REPEAT ;
        -> END;//
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 09:36:14 [employees]> DELIMITER ;
    (gcdb@localhost) 09:36:15 [employees]> call proc_repeat(5,@p_result);
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 09:36:27 [employees]> select @p_result;
    +-----------+
    | @p_result |
    +-----------+
    |       120 |
    +-----------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 09:36:34 [employees]> call proc_repeat(1,@p_result);
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 09:36:44 [employees]> select @p_result;
    +-----------+
    | @p_result |
    +-----------+
    |         1 |
    +-----------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 09:36:45 [employees]> call proc_repeat(0,@p_result);
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 09:36:49 [employees]> select @p_result;
    +-----------+
    | @p_result |
    +-----------+
    |         0 |
    +-----------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 09:36:51 [employees]>
    
    
    • while 和 repeat对比

    while 和 repeat对比

    6.5 loop循环、LEAVE和ITERATE语句

    -- loop语法
    [begin_label:] LOOP
        statement_list
    END LOOP [end_label]
    
    LEAVE label  
    ITERATE label  
    
    -- ITERATE 和label相结合,表示继续从label处执行
    -- LEAVE   和label相结合,表示从label 标记的代码段离开
    
    

    LOOP语句可以使某些特定的语句重复执行,实现一个简单的循环。但是LOOP语句本身没有停止循环的语句,必须是遇到LEAVE语句等才能停止循环。

    LEAVE语句主要用于跳出循环控制

    ITERATE语句也是用来跳出循环的语句。但是,ITERATE语句是跳出本次循环,然后直接进入下一次循环。

    -- loop 例子
    (gcdb@localhost) 10:23:38 [employees]> DELIMITER //
    (gcdb@localhost) 10:24:15 [employees]> CREATE PROCEDURE proc_loop (IN p_num INT)
        -> BEGIN
        -> t_label : LOOP
        -> SET p_num := p_num + 1 ; -- 参数累加
        -> IF p_num < 10 THEN
        -> ITERATE t_label ;    -- 如果累加的值小于10,继续执行t_label
        -> END IF ;
        -> LEAVE t_label ;      -- 如果>=10则离开这个t_label(loop)
        -> END LOOP t_label ;
        -> SET @x = p_num ;     -- 设置会话级别的变量
        -> END ;//
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 10:24:15 [employees]> DELIMITER ;
    (gcdb@localhost) 10:24:17 [employees]> CALL proc_loop(11);  -- 11>10 执行LEAVE,离开整个循环
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 10:24:28 [employees]> SELECT @x;   
    +------+
    | @x   |
    +------+
    |   12 |
    +------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 10:24:28 [employees]> CALL proc_loop(9);  -- 9<10 ,累加1次后>=10为true,执行ITERATE离开循环,再对比执行LEAVE,离开整个循环
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 10:24:34 [employees]> SELECT @x;     -- 累加到10的 param_1 赋值给 x, 即为10
    +------+
    | @x   |
    +------+
    |   10 |   
    +------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 10:24:37 [employees]> CALL proc_loop(8);  -- 8<10 ,累加2次后>=10为true,离开循环,执行ITERATE离开循环,再对比执行LEAVE,离开整个循环
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 10:24:40 [employees]> SELECT @x;
    +------+
    | @x   |
    +------+
    |   10 |
    +------+
    1 row in set (0.00 sec)
    
    (gcdb@localhost) 10:24:41 [employees]>
    

    7. 查看、修改、删除存储过程和函数

    7.1 查看存储过程和函数

    --SHOW STATUS语句查看存储过程和函数的状态
    
    (gcdb@localhost) 11:00:51 [employees]> show procedure status like 'proc_loop'G;
    *************************** 1. row ***************************
                      Db: employees
                    Name: proc_loop
                    Type: PROCEDURE
                 Definer: gcdb@%
                Modified: 2017-12-18 10:24:15
                 Created: 2017-12-18 10:24:15
           Security_type: DEFINER
                 Comment:
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: utf8_general_ci
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    -- SHOW CREATE语句查看存储过程和函数的定义
    
    (gcdb@localhost) 11:05:23 [employees]> show create procedure proc_loop G;
    *************************** 1. row ***************************
               Procedure: proc_loop
                sql_mode:
        Create Procedure: CREATE DEFINER=`gcdb`@`%` PROCEDURE `proc_loop`(IN p_num INT)
    BEGIN
    t_label : LOOP
    SET p_num := p_num + 1 ;
    IF p_num < 10 THEN
    ITERATE t_label ;
    END IF ;
    LEAVE t_label ;
    END LOOP t_label ;
    SET @x = p_num ;
    END
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: utf8_general_ci
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    --从information_schema.Routines表中查看存储过程和函数的信息
    
    (gcdb@localhost) 11:08:15 [employees]> SELECT * FROM information_schema.Routines  WHERE ROUTINE_NAME='proc_loop' G;
    *************************** 1. row ***************************
               SPECIFIC_NAME: proc_loop
             ROUTINE_CATALOG: def
              ROUTINE_SCHEMA: employees
                ROUTINE_NAME: proc_loop
                ROUTINE_TYPE: PROCEDURE
                   DATA_TYPE:
    CHARACTER_MAXIMUM_LENGTH: NULL
      CHARACTER_OCTET_LENGTH: NULL
           NUMERIC_PRECISION: NULL
               NUMERIC_SCALE: NULL
          DATETIME_PRECISION: NULL
          CHARACTER_SET_NAME: NULL
              COLLATION_NAME: NULL
              DTD_IDENTIFIER: NULL
                ROUTINE_BODY: SQL
          ROUTINE_DEFINITION: BEGIN
    t_label : LOOP
    SET p_num := p_num + 1 ;
    IF p_num < 10 THEN
    ITERATE t_label ;
    END IF ;
    LEAVE t_label ;
    END LOOP t_label ;
    SET @x = p_num ;
    END
               EXTERNAL_NAME: NULL
           EXTERNAL_LANGUAGE: NULL
             PARAMETER_STYLE: SQL
            IS_DETERMINISTIC: NO
             SQL_DATA_ACCESS: CONTAINS SQL
                    SQL_PATH: NULL
               SECURITY_TYPE: DEFINER
                     CREATED: 2017-12-18 10:24:15
                LAST_ALTERED: 2017-12-18 10:24:15
                    SQL_MODE:
             ROUTINE_COMMENT:
                     DEFINER: gcdb@%
        CHARACTER_SET_CLIENT: utf8
        COLLATION_CONNECTION: utf8_general_ci
          DATABASE_COLLATION: utf8_general_ci
    1 row in set (0.01 sec)
    
    ERROR:
    No query specified
    

    7.2 修改存储过程和函数

    修改存储过程和函数是指修改已经定义好的存储过程和函数。MySQL中通过ALTER PROCEDURE语句来修改存储过程。通过ALTER FUNCTION语句来修改存储函数。

    MySQL中修改存储过程和函数的语句的语法形式如下:
    ALTER {PROCEDURE | FUNCTION} sp_name [characteristic ...]  
    characteristic:  
    { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }  
    | SQL SECURITY { DEFINER | INVOKER }  
    | COMMENT 'string' 
    

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

    characteristic参数指定存储函数的特性;

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

    NO SQL表示子程序中不包含SQL语句;

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

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

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

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

    INVOKER表示调用者可以执行;

    COMMENT 'string'是注释信息

    说明:修改存储过程使用ALTER PROCEDURE语句,修改存储函数使用ALTER FUNCTION语句。但是,这两个语句的结构是一样的,语句中的所有参数都是一样的。而且,它们与创建存储过程或函数的语句中的参数也是基本一样的。

    (gcdb@localhost) 11:13:05 [employees]> show create procedure num_from_employeesG;
    *************************** 1. row ***************************
               Procedure: num_from_employees
                sql_mode:
        Create Procedure: CREATE DEFINER=`gcdb`@`%` PROCEDURE `num_from_employees`(in emp_id int,out count_numint)
        READS SQL DATA  --原来READS SQL DATA
    begin
    	select count(*) into count_num from employees where emp_no = emp_id;
    end
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: utf8_general_ci
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    (gcdb@localhost) 11:13:20 [employees]> alter procedure num_from_employees modifies sql data sql security inUvoker;   --修改成MODIFIES SQL DATA
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:15:15 [employees]> show create procedure num_from_employeesG;
    *************************** 1. row ***************************
               Procedure: num_from_employees
                sql_mode:
        Create Procedure: CREATE DEFINER=`gcdb`@`%` PROCEDURE `num_from_employees`(in emp_id int,out count_numint)
        MODIFIES SQL DATA    --修改成MODIFIES SQL DATA
        SQL SECURITY INVOKER
    begin
    	select count(*) into count_num from employees where emp_no = emp_id;
    end
    character_set_client: utf8
    collation_connection: utf8_general_ci
      Database Collation: utf8_general_ci
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    

    7.3 删除存储过程和函数

    (gcdb@localhost) 11:15:19 [employees]> drop procedure  num_from_employees;
    Query OK, 0 rows affected (0.00 sec)
    
    (gcdb@localhost) 11:22:21 [employees]> show create procedure num_from_employeesG;
    ERROR 1305 (42000): PROCEDURE num_from_employees does not exist
    ERROR:
    No query specified
    
    (gcdb@localhost) 11:22:25 [employees]>
    
    
  • 相关阅读:
    UVA1349 Optimal Bus Route Design 最优巴士路线设计
    POJ3565 Ants 蚂蚁(NEERC 2008)
    UVA1663 Purifying Machine 净化器
    UVa11996 Jewel Magic 魔法珠宝
    NEERC2003 Jurassic Remains 侏罗纪
    UVA11895 Honorary Tickets
    gdb调试coredump(使用篇)
    使用 MegaCLI 检测磁盘状态并更换磁盘
    员工直接坦诚直来直去 真性情
    山东浪潮超越3B4000申泰RM5120-L
  • 原文地址:https://www.cnblogs.com/gczheng/p/8056689.html
Copyright © 2011-2022 走看看