mysql 存储过程与函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/daicooper/article/details/84865270

 

什么是存储过程和函数

    存储过程和函数是事先经过编译存储在数据库中的一段 SQL语句的集合,调用存储过程和函数可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器中的传输,对于提高数据处理的效率是有好处的。

    存储过程和函数的区别在于:函数必须有返回值,而存储过程没有,存储过程的参数可以使用 IN,OUT,INOUT 类型,而函数的参数只能是IN 类型的。如果有函数从其他类型的数据库迁移到 MySQL,那么就有可能因此需要将函数改造成存储过程。

存储过程和函数的相关操作

    在对存储过程或函数进行相关操作时,需要首先确认用户是否具有相关的权限。例如,创建存储过程或者函数需要 CREATE ROUTINE 权限。修改或者删除存储过程或者函数需要 ALTER ROUTINE 权限,执行存储过程或者函数需要 EXECUTE 权限。

创建 或 修改 存储过程 或 函数

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


CREATE FUNCTION sp_name ([func_parameter[,....]])
    RETURNS type
    [characteristic...] routine_body



    proc_parameter:
    [IN | OUT | INOUT ] param_name type
    
    func_parameter:
    param_name type

    type:
    Any valid MySQL data type 

    characteristic:
    LANQUAGE SQL
    | [NOT] DETERMINISTIC 
    | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
    | SQL SECURITY { DEFINER | INVOKER}
    | COMMENT 'string'
    
    routine_body:
     Valid SQL procedure statement or statements
    

ALTER { PROCEDURE | FUNCTION } sp_name [characteristic...]

    characteristic:

      { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
    | SQL SECURITY { DEFINER | INVOKER}
    | COMMENT 'string'  

 

    调用过程的语法如下:

CALL sp_name([parameter[,....]]);

    MySQL 的存储过程和函数中允许包含 DDL 语句,也允许在存储过程中执行 提交 (Commit,即确认之前的修改或者 回滚 (Rollback, 即放弃之前的修改),但是存储过程和函数中不允许执行 LOAD DATA INFILE 语句。此外,存储过程和函数中允许调用其它的过程或者函数。

下面创建了一个新的过程 proc_adder:

mysql > DELIMITER $$
mysql > CREATE PROCEDURE `proc_adder`(IN a int, IN b int, OUT sum int)
      > BEGIN
      > DECLARE c int;
      > if a is null then
      >    set a = 0; 
      > end if;
      > if b is null then
      >     set b = 0;
      > end if;
      > set sum  = a + b;
      > END $$
Query Ok, 0 rows affected (0.00 sec)
mysql >
mysql > DELIMITER ;


     上面的存储过程比较简单做的是一个加法运算。

     通常我们在执行创建过程和函数之前,都会通过  【DELIMITER $$ 】命令将语句的结束符由 【;】 修改成其它符号,这里使用的是 【$$】 ,这样在过程和函数中的【;】 就不会被 MySQL 解释成语句的结束而提示错误。在存储过程或者函数创建完毕,通过 【DELIMITER ;】 再将结束符改回成 【;】。

  执行结果:

mysql> set @b=5;
Query OK, 0 rows affected (0.00 sec)

mysql>  call proc_adder(2,@b,@s);
Query OK, 0 rows affected (0.00 sec)

mysql> select @s as sum;
+------+
| sum  |
+------+
|    7 |
+------+
1 row in set (0.00 sec)

mysql> 

    可以看到,调用存储过程与直接执行SQL语句的效果是相同的,但是存储过的好处在于处理逻辑都封装在数据库端,调用值不需要了解中间的处理逻辑,一旦处理逻辑发生变化,只需要修改存储过程即可,而对调用者的程序完全没有影响。

    另外,和视图的创建语法稍有不同,存储过程和函数的 CREATE 语法不支持用  CRATE OR REPLACE 对存储过程和函数进行修改,如果需要对已有的存储过程和函数进行修改,需要执行ALTER 语法。

    下面对 characteristic 特征值的部分进行简单说明:

  • LANAUAGE  SQL: 说明下面过程的 body 是使用SQL 语言编写,这条是系统默认的,为今后 MySQL 会支持的除 SQL 外的其它语言支持的存储过程而准备。
  • [NOT] DETERMINISTIC: DETERMINISTIC 确定的,即每次输入一样,输出也一样的程序,NOT DETERMINISTIC 非确定的,默认是非确定的,当前这个特征值还没有被优化程序使用。
  • { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }: 这些特征值提供子程序使用数据的内在信息,这些特征值目前只是提供给服务器,并没有根据这些特征值来约束过程实际使用数据的情况。CONTAINS SQL:表示子程序包含读或写数据的语句,NO SQL:表示子程序不包含 SQL 语句,READS SQL DATA: 表示子程序包含读数据的语句,但不包含写数据的语句,MODIFIES SQL DATA: 表示子程序包含写数据的语句,如果这些特征值没有明确给定,默认使用的值是:CONTAINS SQL。
  • SQL SECURITY { DEFINER | INVOKER}: 可以用来指定子程序该用创建子程序的许可来执行,还是使用调用的者的许可来执行,默认值是 DEFINER。
  • COMMENT 'string' :存储过程或是函数的注释信息。

删除存储过程或是函数

    一次只能删除一个存储过程或者函数,删除存储过程或者函数要有该过程或者函数的 ALTER ROUTINE权限,具体语法如下:

DROP { PROCEDURE | FOUNCTION }{IF EXISTS} sp_name;

查看存储过程或者函数

show { PROCEDURE | FOUNCTION } STATUS [like 'pattern'];

       查看存储过程 proc_adder 的信息:


mysql> SHOW PROCEDURE STATUS LIKE 'proc_adder'\G;
*************************** 1. row ***************************
                  Db: daicooper
                Name: proc_adder
                Type: PROCEDURE
             Definer: [email protected]
            Modified: 2018-12-06 14:07:52
             Created: 2018-12-06 14:07:52
       Security_type: DEFINER
             Comment: 
character_set_client: utf8mb4
collation_connection: utf8mb4_unicode_ci
  Database Collation: utf8_general_ci
1 row in set (0.00 sec)

ERROR: 
No query specified

mysql> 

查看存储过程或者函数的定义

show CREATE { PROCEDURE | FOUNCTION } sp_name;

     查看存储过程 proc_adder 的定义:


mysql> SHOW PROCEDURE STATUS LIKE 'proc_adder'\G;
*************************** 1. row ***************************
                  Db: daicooper
                Name: proc_adder
                Type: PROCEDURE
             Definer: [email protected]
            Modified: 2018-12-06 14:07:52
             Created: 2018-12-06 14:07:52
       Security_type: DEFINER
             Comment: 
character_set_client: utf8mb4
collation_connection: utf8mb4_unicode_ci
  Database Collation: utf8_general_ci
1 row in set (0.00 sec)

ERROR: 
No query specified

mysql> 

通过 information_schema.routines 了解存储过程和函数的信息

    查看存储过程 proc_adder 的定义:

mysql> select * from information_schema.routines where ROUTINE_NAME = 'proc_adder'\G;
*************************** 1. row ***************************
           SPECIFIC_NAME: proc_adder
         ROUTINE_CATALOG: def
          ROUTINE_SCHEMA: daicooper
            ROUTINE_NAME: proc_adder
            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
    #Routine body goes here...

    DECLARE c int;
    if a is null then set a = 0; 
    end if;
  
    if b is null then set b = 0;
    end if;

    set sum  = a + b;
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: 2018-12-06 14:07:52
            LAST_ALTERED: 2018-12-06 14:07:52
                SQL_MODE: STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
         ROUTINE_COMMENT: 
                 DEFINER: [email protected]
    CHARACTER_SET_CLIENT: utf8mb4
    COLLATION_CONNECTION: utf8mb4_unicode_ci
      DATABASE_COLLATION: utf8_general_ci
1 row in set (0.00 sec)

ERROR: 
No query specified

mysql> 

变量的使用

     存储过程和函数中可以使用变量,变量是不区分大小写的。

1:变量的定义

    通过 DECLARE 可以定义一个局部变量,改变量的作用范文只能在 BEGIN....END 块中,可以用在嵌套的块中。

    变量的定义必须写在复合语句的开头,并且在任何其它语句的前面,可以一次声明多个相同类型的变量。如果需要,可以使用 DEFAULT 赋默认值。

    定义一个变量的语法如下:

DELARE var_name [,....] type [DEFAULT value]

    例如:定义一个 DATE 类型的变量 ,名称是 last_month_start

DELARE last_month_start DATE;

1:变量的赋值

    变量可以直接赋值,或者通过查询赋值。直接赋值使用 SET ,可以赋值常量或者表达式,语法如下:

set last_month_start = DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH);

    也可以通过查询将结果赋值给变量, 这要求查询返回的结果必须只有一行。具体语法如下:

select col_name [,....]  INTO var_name [,....] table_expr

    通过查询结果赋值给 last_month_start :

select DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH) INTO last_month_start from DUAL

流程控制

    可以使用 IF, CASE, LOOP, LEAVE, ITERATE, REPEAT,WHILE 语句进行流程控制。

    1:IF 语句

    if 实现条件判断,满足不同的条件执行不同的语句列表,具体语法:

IF search_condition THEN statement_list
    [ELSEIF search_condition THEN statement_list] ....
    [ELSE statement_list]
END IF 

2:CASE 语句

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

    比如 ,过程 proc_case 判断输入的参数值是 0 还是 1 或是 other:

CREATE PROCEDURE `proc_case`(IN type int)
BEGIN
   #Routine body goes here...
   DECLARE c varchar(500);
   CASE type
   WHEN 0 THEN
       set c = 'param is 0';
   WHEN 1 THEN
       set c = 'param is 1';
   ELSE
       set c = 'param is others, not 0 or 1';
   END CASE;
   select c;
END

3:LOOP 语句

    LOOP 语句实现简单的循环,退出循环的条件需要使用其它的语句定义,通常可以使用 LEAVE 语句实现,具体语法:

[begin_label:] LOOP
    statement_list
END LOOP [end_label]

    如果不在 statement_list 中增加退出循环的语句,那么 LOOP 语句可以用来实现简单的死循环。

4:LEAVE 语句

    用来从标注的流程构造中退出,通常和 BEGIN...END 或循环一起使用。

    下面是一个 LOOP 和 LEAVE 一起使用的一个简单的例子,循环一百次向 test 表中插入记录,当插入 100 条记录后退出循环。

CRATE PROCEDURE proc_Loop(OUT sum INT)
    BEGIN
        set @x = 0;
        ins:LOOP
            set @x = @x +1;
            IF  @x = 100 THEN 
                LEAVE  ins;
            END IF;
            set sum = sum + @x;
        END LOOP;
        select sum;
    END

  执行结果

mysql> call proc_Loop(@sum);
+------+
| sum  |
+------+
| 4950 |
+------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

猜你喜欢

转载自blog.csdn.net/daicooper/article/details/84865270