10.1创建存储过程和函数

内容来自《MySQL从入门到精通》清华大学出版社一书的内容,随看书随打打笔记,会不断补充 

       存储程序可以分为存储过程和函数,MySQL中创建存储过程和函数使用的语句分别是:CREATE PROCEDURE和CREATE FUNCTION。使用CALL语句来调用存储过程,只能用输出变量返回值。函数可以从语句外调用(即通过引用函数名),也能返回标量值。存储过程也可以调用其它存储过程。

10.1.1创建存储过程

       创建存储过程,需要使用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指定存储过程的特性,有以下取值:

1.LANGUAGE SQL:说明routine_body部分是由SQL语句组成的,当前系统支持的语言为SQL,SQL是LANGUAGE特性的唯一值。

2.[NOT] DETERMINISTIC:指明存储过程执行的结果是否确定。DETERMINISTIC表示结果是确定的。每次执行存储过程时,相同的输入会得到相同的输出,NOT DETERMINISTIC表示结果是不确定的,相同的输入可能得到不同的输出,如果没有指定任意一个值,默认为 NOT DETERMINISTIC。

3.{CONTAINS SQL|NOT SQL|READS SQL DATA|MODIFIES SQL DATA};指明子程序使用SQL语句的限制。CONTAINS SQL表明子程序包含SQL语句,但是不包含读写数据的语句;NOT SQL表明子程序不包含SQL语句;READS SQL DATA说明子程序包含读数据的语句;MODIFIED SQL DATA表明子程序包含写数据的语句。默认情况下,系统会指定为CONTAINS SQL。

4.SQL SECURITY{DEFINER|INVOKER}:指明谁有权限来执行。DEFINER表示只有定义者才能执行。INVOKER表示拥有权限的调用者可以执行。默认情况下,系统指定为DEFINER。

5.COMMENT'string':注释信息,可以用来描述存储过程或函数。

       routine_body是SQL代码的内容,可以用BEGIN..END来表示SQL代码的开始和结束。

       编写存储过程并不是件简单的事情,可能存储过程中需要复杂的SQL语句,并且要有创建存储过程的权限;但是使用存储过程将简化操作,减少冗余的操作步骤,同时,还可以减少操作过程中的失误,提高效率,因此存储过程是非常有用的,而且应该尽可能地学会使用。

       下面例子演示了存储过程的内容,名称为AvgFruitPrice,返回所有水果的平均价格,输入代码如下:

CREATE PROCEDURE AvgFruitPrice()
BEGIN
SELECT AVG(f_price) AS avgprice
FROM fruits;
END;

       此过程没有参数,但后面仍需要()。BEGIN和END语句用来限定存储过程体,过程本身是一个简单的SELECT语句。

例:创建查看fruits表的存储过程,代码如下:

CREATE PROCEDURE Proc()
     BEGIN
        SELECT * FROM fruits
     END ;

       这行代码创建了一个查看fruits表的存储过程,每次调用这个存储过程的时候都会执行SELECT语句查看表的内容,代码的执行过程如下:

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

 DELIMITER ;

使用CALL语句调用,与SELECT*FROM fruits效果相同

10.1创建存储过程和函数_第1张图片

        ‘  DELIMITER //’ 语句的作用是将结束符设置为‘//’,因为默认的语句结束符是‘;’。为了避免与存储过程中SQL语句结束符相冲突,需要使用 DELIMITER 改变存储过程的结束符,并以END// 结束存储过程。存储过程定义完毕之后再使用‘ DELIMITER;’恢复默认结束符。可以指定其它符号作为结束符。

 例:创建名称为CountProc的存储过程

CREATE PROCEDURE CountProc(OUT paraml INT)
BEGIN
SELECT COUNT(*) INTO paraml FROM fruits;
END;

         上述代码的作用是创建一个获取fruits表记录条数的存储过程,名称是CountProc,COUNT(*)计算后把结果放入参数paraml中。代码的执行结果如下:

 DELIMITER // 
 CREATE PROCEDURE CountProc(OUT param1 INT)
  BEGIN
  SELECT COUNT(*) INTO param1 FROM fruits;
  END //

 DELIMITER ;

     当使用DELIMITER命令时,因该避免使用反斜杠(‘\’)字符,因为反斜线是MySQL的转义字符。

10.1.2创建存储函数

       创建存储函数,需要使用CREATE FUNCTION语句。

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

          CREATE FUNCTION为用来创建存储函数的关键字;func_name为存储函数的名称;func_parameter为指定存储过程的参数列表。参数列表形式如下:

[IN|OUT|INOUT] param_name type

       其中IN表示输入参数,OUT表示输出参数,INOUT表示既可以输入也可以输出;param_name表示参数名称;type表示参数的类型,该类型可以是MySQL数据库中的任意类型。

       RETURNS type语句表示函数返回数据的类型;characteristic指定存储函数的特性,取值与创建存储过程时相同。

例:创建存储函数,名称为NameByZip,该函数返回SELECT语句的查询结果,数值类型为字符串型,代码如下:

CREATE FUNCTION NameByZip ()
 RETURNS CHAR(50)
 RETURN  (SELECT s_name FROM suppliers WHERE s_call= '48075');

创建一个存储函数,参数定义为空,返回一个INT类型的结果。这样执行会出现错误。

 DELIMITER //
 CREATE FUNCTION NameByZip()
 RETURNS CHAR(50)
 RETURN   (SELECT s_name FROM suppliers WHERE s_call= '48075');
 //

           如果在存储函数中的RETURN语句返回一个类型不同于函数的RETURNS子句中指定类型的值,返回值将被强制为恰当类型。比如,如果一个函数返回一个ENUM值或SET值,但是RETURN语句返回一个整数,对于SET成员集的相应的ENUM成员,从函数返回的值是字符串。 

     指定参数为IN,OUT或INOUT只对PROCEDURE是合法的。(FUNCTION中总是默认为IN参数)。RETURNS子句只能对FUNCTION做指定,对函数而言这是强制的,它用来指定函数的返回类型,而且函数体必须包含一个RETURN value语句。 

对于上面出错问题,这是我们开启了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指定一个参数。


在MySQL中创建函数时出现这种错误的解决方法:
set global log_bin_trust_function_creators=TRUE;

10.1.3变量的使用

       变量可以在子程序中声明并使用,这些变量的作用范围是在BEGIN..END程序中,将介绍如何定义变量和为变量赋值。

     1.定义变量

       在存储过程中使用DECLARE语句定义变量

DECLARE var_name[,varname]...date_type [DEFAULT value];

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

例:定义名称为myparam的变量,类型为INT类型,默认值为100,代码如下:
 

     DECLARE  myparam  INT  DEFAULT 100;

      2.为变量赋值

        定义变量之后,为变量赋值可以改变变量的默认值,MySQL中使用SET语句为变量赋值。

SET var_name =expr [,var_name =expr]...;

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

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

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

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

         MySQL中还可以通过SELECT ... INTO为一个或多个变量赋值,语法如下:

SELECT col_name[,...] INTO var_name[,...] table_expr;

         这个SELECT语法把选定的列直接存储到对应位置的变量。col_name表示字段名称;var_name表示定义的变量名称;table_expr表示查询条件表达式,包括表名称和WHERE子句。

例:声明变量fruitname和fruitprice,通过SELECT ... INTO语句查询指定记录并为变量赋值,代码如下:

DECLARE fruitname CHAR(50);
DECLARE fruitprice DECIMAL(8,2);

SELECT f_name,f_price INTO fruitname, fruitprice
FROM fruits WHERE f_id ='a1';

10.1.4定义条件和处理程序

       特定条件需要特定处理。这些条件可以联系到错误,以及子程序中的一般流程控制。定义条件是事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时应当采取的处理方式,并且保证存储过程或函数在遇到警告或错误时能继续执行。这样可以增强存储程序处理问题的能力,避免程序异常停止运行。使用DECLARE关键字来定义条件和处理程序。

       1.定义条件

        定义条件使用DECLARE语句

DECLARE condition_name CONDITION FOR [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为数值类型错误代码。例如:ERROR 1142(42000)中,sqlstate_value的值是42000,MySQL_error_code的值是1142。

        这个语句指定需要特殊处理的条件。它将一个名字和指定的错误条件管理起来。这个名字可以随后被用在定义处理程序的DECLARE HANDLER语句中。

例:定义"ERROR 1148(42000)"错误,名称为command_not_allowed。可以用两种不同的方法来定义,代码如下:

//方法一:使用sqlstate_value 
DECLARE  command_not_allowed CONDITION FOR SQLSTATE '42000';
//方法二:使用mysql_error_code 
DECLARE  command_not_allowed CONDITION  FOR  1148

2.定义处理程序

        定义处理程序时,使用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个值:CONTINUE,EXIT和UNDO。CONTINUE表示遇到错误不处理,继续执行;EXIT遇到错误马上退出;UNDO表示遇到错误后撤回之前的操作。

        condition_value表示错误类型,可以有以下取值:

#  SQLSTATE[VALUE]sqlstate_value:包含5个字符的字符串错误值。

#  condition_name:表示DECLARE CONDITION定义的错误条件名称。

#  SQL WARNING:匹配所有以01开头的SQLSTATE错误代码。

# NOT FOUND:匹配所有以02开头的SQLSTATE错误代码。

# SQLEXCEPTION:匹配所有没有被SQLWARING或NOT FOUND 捕获的SQLSTATE错误代码。

# MySQL_error_code:匹配数值类型错误代码。

         sp_statement参数为程序语句段,表示在遇到定义的错误时,需要执行的存储过程或函数。

例:定义处理程序的几种方式,代码如下:

//方法一:捕获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=' NO_SUCH_TABLE ';

//方法四:使用SQLWARNING
DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';

//方法五:使用NOT FOUND
DECLARE EXIT HANDLER FOR NOT FOUND SET @info=' NO_SUCH_TABLE ';

//方法六:使用SQLEXCEPTION
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR'; 

      上述代码是六种定义处理程序的方法:

        第一种方法是捕获sqlstate_value值。如果遇到sqlstate_value值为“42S02”,执行CONTINUE操作,并且输出“NO_SUCH_TABLE”信息。

        第二种方法是捕获MySQL_error_code值。如果遇到MySQL_error_code值为1146,执行CONTINUE操作,并且输出“NO_SUCH_TABLE”信息。

        第三种方法是先定义条件,然后再调用条件。这里先定义no_such_table条件,遇到1146错误就执行CONTINUE操作。

        第四种方法是使用SQLWARING。SQLWARING捕获所有以01开头的sqlstate_value,然后执行EXIT操作,并且输出”ERROR“信息。

        第五种方法是使用NOT FOUND。NOT FOUND 捕获所有以02开头的sqlstate_value,然后执行EXIT操作,并且输出“NO_SUCH_TABLE”信息。

        第六种方法是使用SQLEXCEPTION。SQLEXCEPTION捕获所有没有被SQLWARNING或NOT FOUND捕获的sqlstate_value值,然后执行EXIT操作,并且输出”ERROR“信息。

例:定义条件和处理程序,具体执行的过程如下:

 CREATE TABLE testt (s1 int,primary key (s1));
Query OK, 0 rows affected (0.00 sec)

 DELIMITER //
 
 CREATE PROCEDURE handler1demo ()
      BEGIN
       DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
       SET @x = 1;
       INSERT INTO testt VALUES (1);
       SET @x = 2;
       INSERT INTO testt VALUES (1);
       SET @x = 3;
     END;
     //




 DELIMITER ;

调用存储过程并查看调用过程结果:

10.1创建存储过程和函数_第2张图片

      @x是1个用户变量,执行结果@x等于3,这表明MySQL被执行到程序的末尾。如果”  DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;“这一行不存在,第2个INSERT因PRIMARY KEY强制而失败之后,MySQL可能已经采取了默认EXIT路径,并且SELECT @x可能已经返回2。

10.1.5光标的使用

       查询语句可能返回多条记录,如果数据量非常大,需要在存储过程和储存函数中使用光标来逐条读取查询结果集中的记录。应用程序可以根据需要滚动或浏览其中的数据。介绍如何声明,打开,使用和关闭光标。

1.声明光标

        MySQL中使用DECLARE关键字声明光标。

DECLARE cursor_name CURSOR FOR select_statement

        cursor_name为光标名称;select_statement参数表示SELECT语句的内容,返回一个用于创建光标的结果集。

例:声明名称为cursor_fruit的光标,代码如下:

DECLARE cursor_fruit CURSOR FOR SELECT f_name, f_price FROM fruits ;

2.打开光标

       语法如下:

OPEN cursor_name{光标名称}

例:利用该语法打开上面创建的光标

OPEN cursor_fruit;

3.使用光标

     使用光标语法如下:

FETCH cursor_name INTO var_name [,var_name]...{参数名称}

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

例:使用名称为cursor_fruit的光标。将查询出来的数据存入fruit_name和fruit_price这两个变量中,代码如下:

FETCH  cursor_fruit INTO fruit_name, fruit_price ;

       上面的示例中,将光标cursor_fruit中SELECT语句查询出来的信息存入fruit_name和fruit_price中。fruit_name和fruit_price必须在前面已经定义。

4.关闭光标

    关闭光标语法如下:

CLOSE cursor_name{光标名称}

     这个语句关闭先前打开的光标。如果未被明确地关闭,光标在它被声明的复合语句的末尾被关闭。 

例:关闭名称为cursor_fruit的光标,代码如下:

CLOSE  cursor_fruit; 

MySQL中光标只能在存储过程和函数中使用

10.1.6流程控制的使用

        流程控制语句用来根据条件控制语句的执行。MySQL中用来构造控制流程的有:IF,CASE,LOOP,LEAVE,ITERATE,REPEAT和WHILE语句。

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

1.IF语句

        IF 的语句包含多个条件判断,根据判断的结果为TRUE还是FALSE执行相应的语句。

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

       IF实现了一个基本的条件构造。如果expr_condition求值为真,相应的SQL语句列表被执行;如果没有expr_condition匹配,测ELSE子句里的语句列表被执行。statement_list可以包含一个或多个语句。

MySQL中还有一个IF()函数,它不同于这里描述的IF语句。

例:IF语句的示例,代码如下:

IF val IS NULL
  THEN SELECT 'val is NULL';
  ELSE SELECT 'val is not NULL';
END IF;
    判断val值是否为空,为空则输出字段 val is null,否则为VAL IS NOT NULL

2.CASE语句

       CASE是另一个进行条件判断的语句,该语句有两种格式,第一种格式如下:

CASE case_expr
     WHEN when_value THEN statement_list
     [WHEN when_value THEN statement_list]...
     [ELSE statement_list]
END CASE

        其中,case_expr参数表示条件判断的表达式,决定了哪一个WHEN子句会被执行;when_value参数表示表达式可能的值,如果某个when_value表达式与case_expr表达式结果相同,则执行对应THEN关键字后的statement_list中的语句;statement_list参数表示不同when_value值的执行语句。

例:使用CASE流程控制语句的第1种格式,判断val值等于1、等于2,或者两者都不等,语句如下:

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


        expr_condition 参数表示条件判断语句;statement_list参数表示不同条件的执行语句。该语句中,WHEN语句将被逐个执行,直到某个expr_condition表达式为真,则执行对应THEN关键字后面的statement_list语句。如果没有匹配条件,执行ELSE的语句。

      用在存储程序里的CASE语句与”流程控制函数“里描述的CASE表达式里的CASE语句有轻微不同。这里的CASE不能有ELSE NULL 子句,并且用END CASE代替END终止。

例:使用CASE流程控制语句的第2种格式,判断val是否为空、小于0、大于0或者等于0,语句如下:

CASE
  WHEN val IS NULL THEN SELECT 'val is NULL';
  WHEN val < 0 THEN SELECT 'val is less than 0';
  WHEN val > 0 THEN SELECT 'val is greater than 0';
  ELSE SELECT 'val is 0';
END CASE;

3.LOOP语句

        LOOP循环语句用来重复执行某些语句,与IF和CASE相比,LOOP只是创建一个循环操作的过程,并不进行条件判断。LOOP中的语句一直被重复执行直到循环退出,跳出循环过程,使用LEAVA子句。

[loop_lable1:]LOOP
     statement_list
END LOOP [loop_label]

       loop_lable表示LOOP语句的标注名称,该参数可以省略;statement_list参数表示需要循环执行的语句。

例:使用LOOP语句进行循环操作,id值小于等于10之前,将重复执行循环过程

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+1的操作,当id值小于10时,循环重复执行;当id值大于或等于10时,使用LEAVE语句退出循环。LOOP循环都以END LOOP结束。

4.LEAVE语句

      LEAVE语句用来退出任何被标注的流程控制构造。语法:

LEAVE label
label参数表示循环的标志。LEAVE和BEGIN..END或循环一起被使用。

例:使用LEAVE语句退出循环,代码如下:

add_num: LOOP  
SET @count=@count+1;
IF @count=50 THEN LEAVE add_num ;
END LOOP add_num ; 
该示例循环执行count加1的操作。当count的值等于50时,使用LEAVE语句跳出循环。

5.ITERATE语句

       ITERATE语句将执行顺序转到语句开头处,语句基本格式如下:

ITERATE label

       ITERATE只能出现在LOOP,REPEAT和WHILE语句内。ITERATE意思为”再次循环“,label参数表示循环的标志。ITERATE语句必须跟在循环标志前面 。

例:ITERATE语句示例,代码如下:

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

        p1=0,如果p1的值小于10时,重复执行p1加1操作;当p1大于等于10并且小于20时,打印消息”p1 is between 10 and 20“;当p1大于20时,退出循环。

6.REPEAT语句

     

 REPEAT语句创建一个带条件判断的循环过程,每次语句执行完毕之后,会对条件表达式进行判断,如果表达式为真,则循环结束;否则重复执行循环中的语句。基本格式如下:

[repeat_label:]REPEAT
     statement_list
UNTIL expr_condition
END REPEAT [repeat_label]

repeat_label为REPEAT语句的标注名称,该参数可以省略;REPEAT语句内的语句或语句群被重复,直至expr_condition的值为真

例:REPEAT语句示例,id值小于等于10之前,将重复执行循环过程

DECLARE id INT DEFAULT 0;
REPEAT
SET id = id + 1;
UNTIL  id >= 10
END REPEAT; 

        该示例循环执行id加1的操作。当id值小于10时,循环重复执行;当id值大于或者等于10时,使用LEAVE语句退出循环。REPEAT循环都以END REPEAT结束。

7.WHILE语句

       WHILE语句创建一个带条件判断的循环过程,与REPEAT不同,WHILE在执行语句时,先对指定的表达式进行判断,如果为真,则执行循环内语句,否则退出循环。基本格式如下:

[while_label:] WHILE expr_condition DO
     statement_list
END WHILE [while_label]

while_label为while语句标注名称:expr_condition为进行判断的表达式,如果表达式结果为真,WHILE语句内的语句或语句群被执行,直至expr_condition为假,退出循环。

例:WHILE语句示例,id值小于等于10之前,将重复执行循环过程

DECLARE i INT DEFAULT 0;
WHILE i < 10 DO
SET i = i + 1;
END WHILE;

 

你可能感兴趣的:(MySQL)