• 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]>
    
    
  • 相关阅读:
    数论笔记
    哈哈哈
    闭关修炼屯题中,期末考完A
    acm几何
    POJ
    Educational Codeforces Round 42 (Rated for Div. 2) D.Merge Equals (优先队列)
    Educational Codeforces Round 42 (Rated for Div. 2) C. Make a Square (dfs)
    牛客网 VVQ 与线段 (优先队列或线段树或RMQ)
    Educational Codeforces Round 41 (Rated for Div. 2) C.Chessboard (DP)
    Educational Codeforces Round 41 (Rated for Div. 2)D. Pair Of Lines
  • 原文地址:https://www.cnblogs.com/gczheng/p/8056689.html
Copyright © 2020-2023  润新知