mysql 存储过程和函数

学习了mysql 索引,接着学习也很常用的存储过程和函数。

1,创建存储过程和函数

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

1,创建存储过程

创建存储过程,需要使用CREATE PROCEDURE语句,基本语法格式如下:

sql 复制代码
CREATE PROCEDURE sp_name([proc_parameter])
[characteristics ...} routine_body

CREATE PROCEDURE为用来创建存储函数的关键字;sp_name为存储过程的名称;proc_parameter为指定存储过程的参数列表,列表形式如下:

sql 复制代码
[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表示结果是确定的。每次执行存储过程时,相同的输入会得到相同的输出。NOTDETERMINISTIC表示结果是不确定的,相同的输入可能得到不同的输出。如果没有指定任意一个值,默认为NOT DETERMINISTIC.

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

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

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

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

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

sql 复制代码
CREATE PROCEDURE AvgFruitPrice()
BEGIN
  SELECT AVG(f_price) As avgprice
  FROM fruits;
END;

上述代码中,此存储过程名为AvgFruitPrice,使用CREATE PROCEDURE AvgFruitPrice (语句定义。此存储过程没有参数,但是后面的()仍然需要。BEGIN和END语句用来限定存储过程体,过程本身仅是一个简单的SELECT语句(AVG为求字段平均值的函数)。

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

sql 复制代码
CREATE PROCEDURE Proc()
BEGIN
  SELECT * FROM fruits;
END;

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

sql 复制代码
DELIMITER//
CREATE PROCEDURE Proc()
BEGIN
  SELECT*FROM fruits;
END //

DELIMITER ; -- 注意这边是有空格的, 缩进就报错了

这个存储过程和使用SELECT 语句查看表的效果得到的结果是一样的,当然存储过程也可以是很多语句的复杂组合,其本身也可以调用其他的函数来组成更加复杂的操作。

调用存储过程:

sql 复制代码
 CALL Proc();

提示:"DELIMITER//"语句的作用是将MySQL 的结束符设置为//,因为MySQL默认的语句结束符号为分号';',为了避免与存储过程中SQL语句结束符相冲突,需要使用DELIMITER改变存储过程的结束符,并以"END //"结束存储过程。存储过程定义完毕之后再使用"DELIMITER;"恢复默认结束符。DELIMITER也可以指定其他符号做为结束符,比如$:

sql 复制代码
DELIMITER $
CREATE PROCEDURE Proc2()
BEGIN
  SELECT*FROM fruits;
END $

【例2】创建名称为CountProc的存储过程,代码如下:

sql 复制代码
CREATE PROCEDURE CountProc (OUT param2 INT)
BEGIN
  SELECT COUNT(*) INTO param2 FROM fruits;
END;

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

sql 复制代码
DELIMITER //
CREATE PROCEDURE CountProc(OUT param2 INT)
BEGIN
  SELECT COUNT(*) INTO param2 FROM fruits;
END //

DELIMITER ;

执行后,调用再查询:

sql 复制代码
 CALL CountProc2(@count);
 SELECT @count;

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

2,创建存储函数

创建存储函数,需要使用CREATE FUNCTION语句,基本语法格式如下:

sql 复制代码
CREATE FUNCTION func_name([fune_parameter])
RETURNS type
[characteristic ...] routine_body

CREATE FUNCTION为用来创建存储函数的关键字;func_name表示存储函数的名称;func parameter为存储过程的参数列表,参数列表形式如下:

sql 复制代码
[IN | OUT | INOUT ] param_name type

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

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

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

sql 复制代码
CREATE FUNCTION NameByZip RETURNS CHAR(50)
RETURN (SELECT s_name FROM suppliers WHERE  s_call = '48075');

创建一个存储函数,参数定义为空,返回一个INT类型的结果。代码的执行结果如下:

sql 复制代码
DELIMITER //
CREATE FUNCTION NameBy2ip()
RETURNS CHAR(50)
RETURN (SELECT s_name FROM suppliers WHERE  s_call= '48075');

DELIMITER ;

创建完后调用:

sql 复制代码
SELECT NameBy2ip();

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

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

3,变量的使用

变量可以在子程序中声明并使用,这些变量的作用范围是在 BEGIN...END程序中。

1,定义变量

在存储过程中使用DECLARE语句定义变量,语法格式如下:

sql 复制代码
DECLARE var_name [,varname].. date_type [DEFAULT value];

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

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

sql 复制代码
DECLARE myparam INT DEFAULT 100;
2,为变量赋值

定义变量之后为变量赋值可以改变变量的默认值MySQL中使用SET语句为变量赋值语法格式如下:

sql 复制代码
SET  var name = expr [, var_name = expr] ....]

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

在存储程序中的SET语句作为预先存在的SET语法的一部分来实现。这允许 SET a=x,b=y, ....这样的扩展语法。其中不同的变量类型(局域声明变量及全局变量〉可以被混合起来。

这也允许把局部变量和一些只对系统变量有意义的选项合并起来。

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

sql 复制代码
DECLARE var1,var2, var3 INT;
SET var1 = 10, var2 = 20;
SET var3 = var1 + var2;

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

sql 复制代码
SELECT col_name [, ...] INTO var_name[, ...] table_expr;

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

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

sql 复制代码
DECLARE fruitname CHAR(50);
DECLARE fruitprice DECIMAL(8,2];
SELECT f_name,f price INTO fruitname, fruitprice 
FROM fruits WHERE f_id = 'al';

4,定义条件和处理程序

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

1,定义条件

定义条件使用DECLARE语句,语法格式如下:

sql 复制代码
DECLARE condition_name CONDITION FOR [condition_type]
[condition type]:
SQLSTATE [VALUE] sglstate_value | mysql_error_code

其中,condition_name参数表示条件的名称;condition_type参数表示条件的类型;sqlstate_value和 MySQL_error_code都可以表示 MySQL 的错误,sqIstate_value为长度为5的字符串类型错误代码,MySQL_error_code为数值类型错误代码。例如:ERROR 1142(42000)中,sqlstate_value的值是42000,MySQL_error_code的值是1142。

这个语句指定需要特殊处理的条件。它将一个名字和指定的错误条件关联起来。这个名字

可以随后被用在定义处理程序的DECLARE HANDLER语句中。

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

sql 复制代码
//方法一:使用sqlstate_value
DECLARE command_not_allowed CONDITION FOR SQLSTATE '42000';


//方法二:使用mysql_error_code
DECLARE command not allowed CONDITION FOR 1146;
2,定义处理程序

定义处理程序时,使用DECLARE语句的语法如下:

sql 复制代码
DECLARE handler type HANDLER FOR condition_value[,...] sp_statementhandler type:
CONTINUE I 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表示遇到错误后撤回之前的操作,MySQL中暂时不支持这样的操作。

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

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

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

SQLWARNING:匹配所有以01开头的SQLSTATE错误代码;

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

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

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

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

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

sql 复制代码
-- 方法一:捕获sqlstate_value
DECLARE CONTINUE HANDLER FOR SQLSTATE '42802' SET  @info='NO_SUCH_TABEE';

-- 方法二;获mysql_error code
DECLARE CONTINUE HANDLER FOR 1146 SET @info='NO_SUCH_TABLE ';

-- 方法三:先定义条件,然后调用
DECLARE no_such TABLE CONDITION FOR 11467;
DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SBT @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';

上述代码是6种定义处理程序的方法。

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

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

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

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

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

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

sql 复制代码
CREATE TABLE test_t(s1 int, primary key (s1));
DELIMITER//
CREATE PROCEDURE handlerdemo()
BEGIN
  DECLARE CONTINUE HANDLER FOR SQLSTATE '23000'SET @x2=1;
  SET @x = 1;
  INSERT INTO test_t VALUES (1);
  SET @x = 2;
  INSERT INTO test_t VALUES (1);
  SET @x = 3;
END //

DELIMITER ;
sql 复制代码
/*调用存储过程*/
CALL handlerdemo();


/*查看调用过程结果*/
SELECT @x;

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

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

5,光标的使用

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

光标必须在声明处理程序之前被声明,并且变量和条件还必须在声明光标或处理程序之前被声明。光标的作用范围也是是在 BEGIN...END程序中。

1,声明光标

MySQL中使用DECLARE 关键字来声明光标,其语法的基本形式如下:

sql 复制代码
DECLARE cursor_name CURSOR FOR select statement;

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

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

sql 复制代码
DECLARE cursor_fruit CURSOR FOR SELECT f_name, f_price FROM fruits;

上面的示例中,光标的名称为cur_fruit,SELECT语句部分从fruits表中查询出f_name和f_price字段的值。

2,打开光标

打开光标的语法如下:

sql 复制代码
OPEN cursor_name [光标名称]

这个语句打开先前声明的名称为cursor_name的光标。

【例11】打开名称为cursor_fruit的光标,代码如下:

sql 复制代码
OPEN cursor_fruit;
3,使用光标

使用光标的语法如下:

sql 复制代码
FETCH cursor_name INTO var_name [, var_name]...(参数名称)

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

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

sql 复制代码
FETCH cursor_fruit INTO fruit_name, fruit _price ;

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

4,关闭光标

关闭光标的语法如下:

sql 复制代码
CLOSE cursor_name(光标名称)

这个语句关闭先前打开的光标。

如果未被明确地关闭,光标在它被声明的复合语句的末尾被关闭。

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

sql 复制代码
CLOSE  cursor_fruit;

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

6,流程控制的使用

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

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

1,IF语句

IF语句包含多个条件判断,根据判断的结果为TRUE或FALSE执行相应的语句,语法格式如下:

sql 复制代码
IF expr_condition THEN statementlist
     [ELSE IF expr_condition THEN statement_list] ...
  [ELSE statementlist]
END IF

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

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

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

sql 复制代码
IF val IS NULL
  THEN SELECT 'val is NULL';
  ELSE SELECT 'val is not NULL';
END IF;

该示例判断val值是否为空,如果val值为空,输出字符串"val is NULL"﹔否则输出字符串"val is not NULL"。IF语句都需要使用END F来结束。

2,CASE语句

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

sql 复制代码
CASE case_expr
  WHEN when_value THEN statement_iist
  [WHEN when_value THEN statement_list] ...
  [ELSE statement_1ist]
END CASE

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

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

sql 复制代码
CASE val
  WHEN 1 THEN SELECT 'val is 1';
  WHEN 2 THEN SELECT 'val is 21';
  ELSE SELECT 'val is not 1 or 2';
END CASE:

当val值为1时,输出字符串"valis 1";当val 值为2时,输出字符串"val is2";否则输出字符串"val is not1 or2"。

CASE语句的第2种格式如下: .

sql 复制代码
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语句与"控制流程函数"里描述的SQL CASE表达式的CASE语句有轻微不同。这里的CASE语句不能有ELSE NULL子句,并且用END CASE替代END来终止。

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

sql 复制代码
CASE
  WHEN val is NULL THEN SELECT 'val is NULL';
  WHEN val < 0 THEN SELECT 'val is less than o';
  WHEN val > 0 THEN SELECT 'val is greater than o';
  ELSE SELECT 'val is0';
END CASE;

当val值为空输出字符串"val is NULL"。当val值小于0时,输出字符串"val is less than0o"﹔当val值大于0时,输出字符串"val is greater than 0"﹔否则输出字符串"val is 0"。

3,LOOP语句

LOOP循环语句用来重复执行某些语句,与F和CASE语句相比,LOOP只是创建一个循环操作的过程,并不进行条件判断。LOOP内的语句一直重复执行直到循环被退出,跳出循环过程,使用LEAVE子句,LOOP语句的基本格式如下:

sql 复制代码
[ 1oop_label:] LOOP
  statement_list
END LOOP [loop_label] 

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

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

sql 复制代码
DECLARE id INT DEFAULT 0;
add_loop: LOOP
SET id - id + 1;
  IF 
    id > - 10 THEN LEAVE add_loop;
  END IE;
END LOOP add_loop;

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

4,LEAVE语句

LEAVE语句用来退出任何被标注的流程控制构造,LEAVE语句基本格式如下:

sql 复制代码
LEAVE label

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

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

sql 复制代码
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语句将执行顺序转到语句段开头处,语句基本格式如下:

sql 复制代码
ITERATE label

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

【例19】ITERATE语句示例,代码如下:

sql 复制代码
DELIMITER //
CREATE PROCEDURE doiterate()
BEGIN
  DECLARE p2 INT DEFAULT 0;
  my_loop: LOOP
    SET p2= p2 +1;
    IF p2< 10 THEN ITERATE my_loop;
    ELSEIF p2 > 20 THEN LEAVE my_loop;
    END IF;
    SELECT 'P2 is between 10 and 20';
  END LOOP my_loop;
END //

DELIMITER ;

调用:

sql 复制代码
CALL doiterate();

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

6,REPEAT语句

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

sql 复制代码
[repeat_label:] REPEAT
statement_list
UNTIL exprcondition
END REPEAT [repeat_label]

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

【例20】REPEAT语句示例, id值小于等于10之前,将重复执行循环过程,代码如下:

sql 复制代码
DECLARE id INT DEFAULT 0;
REPEAT
SET id= id+ 1;
UNTIL id >= 10
END REPEAT;

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

7,WHILE语句

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

sql 复制代码
[while_label:] WHILE expr_condition DO
statement_1ist
END WHILE [while_label]

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

【例21】 WHILE语句示例,i值小于10时,将重复执行循环过程,代码如下:

sql 复制代码
DECLARE i INT DEFAULT 0;
WHILE i < 10 Do
SET i=i+1;
END WHILE;

2,调用存储过程和函数

存储过程已经定义好了,接下来需要知道如何调用这些过程和函数。存储过程和函数有多种调用方法。存储过程必须使用CALL语句调用,并且存储过程和数据库相关,如果要执行其他数据库中的存储过程,需要指定数据库名称,例如CALL dbname.procname。存储函数的调用与MySQL中预定义的函数的调用方式相同。本节介绍存储过程和存储函数的调用,主要包括调用存储过程的语法、调用存储函数的语法,以及存储过程和存储函数的调用实例。

1,调用存储过程

存储过程是通过CALL语句进行调用的,语法如下:

sql 复制代码
CALL sp_name([parameter[,...]])

CALL语句调用一个先前用CREATE PROCEDURE创建的存储过程,其中sp_name为存储过程名称,parameter为存储过程的参数。

【例22】定义名为CountProc1的存储过程,然后调用这个存储过程,代码执行如下:定义存储过程:

sql 复制代码
DELIMITER //
CREATE PROCEDURE CountProc1 (IN sid INT, OUT num INT)
BEGIN
  SELECT COUNT(*) INTO num FROM fruits WHERE s_id = sid;
END //  

调用存储过程;

sql 复制代码
CALL CountProc1(101, @num);

查看返回结果:

sql 复制代码
select @num;

该存储过程返回了指定s_id=101的水果商提供的水果种类,返回值存储在num变量中,使用SELECT查看,返回结果为3。

2,调用存储函数

在MySQL中,存储函数的使用方法与MySQL内部函数的使用方法是一样的。换言之,用户自己定义的存储函数与MySQL内部函数是一个性质的。区别在于,存储函数是用户自己定义的,而内部函数是MySQL的开发者定义的。

【例23】定义存储函数CountProc,然后调用这个函数,代码如下:

sql 复制代码
DELIMITER//
CREATE FUNCTION CountProc (sid INT)
RETURNS INT
BEGIN
RETURN(SELECT COUNT(*) FROM fruits WHERE s_id = sid) ;
END // 


DELIMITER ;

调用存储函数:

sql 复制代码
SELECT CountProc(101);

可以看到,该例与上一个例子中返回的结果相同。虽然存储函数和存储过程的定义稍有不同,但可以实现相同的功能,在实际应用中灵活选择。

3,查看存储过程和函数

MySQL存储了存储过程和函数的状态信息,用户可以使用SHOW STATUS语句或SHOWCREATE语句来查看,也可直接从系统的 information_schema数据库中查询。

1,使用SHOW STATUS语句查看存储过程和函数的状态

SHOW STATUS语句可以查看存储过程和函数的状态,其基本语法结构如下:

sql 复制代码
SHOW (PROCEDURE T FUNCTION] STATUS [LIKE 'pattern')

这个语句是一个MySQL的扩展。它返回子程序的特征,如数据库、名字、类型、创建者及创建和修改日期。如果没有指定样式,根据使用的语句,所有存储程序或存储函数的信息都被列出。PROCEDURE 和 FUNCTION分别表示查看存储过程和函数;LIKE 语句表示匹配存储过程或函数的名称。

【例24】SHOW STATUS语句示例,代码如下:

sql 复制代码
-- 命令行
SHOW PROCEDURE STATUS LIKE 'C%'\G


-- 客户端:
SHOW PROCEDURE STATUS LIKE 'C%';

代码执行如下:

"SHOW PROCEDURE STATUS LIKE 'C%'"语句获取数据库中所有名称以字母'C'开头的存储过程的信息。通过上面的语句可以看到:这个存储函数所在的数据库为test、存储函数的名称为CountProc等一些相关信息。

2,使用SHOW CREATE语句查看存储过程和函数的定义

除了SHOW STATUS之外,MySQL还可以使用SHOW CREATE语句查看存储过程和函数的状态。

sql 复制代码
SHOW CREATE {ROCEDURE | FUNCTION} sp_name

这个语句是一个MySQL 的扩展。类似于SHOW CREATE TABLE,它返回一个可用来重新创建已命名子程序的确切字符串。PROCEDURE和 FUNCTION分别表示查看存储过程和函数: sp_name参数表示匹配存储过程或函数的名称。

【例25】SHOW CREATE语句示例,代码如下:

sql 复制代码
SHOW CREATE FUNCTION test.NameBy2ip;

代码执行结果如下:

执行上面的语句可以得到存储函数的名称为NameBy2ip,sql_mode为 SQL的模式,CreateFunction为存储函数的具体定义语句,还有数据库设置的一些信息。

3,从information_schema.Routines表中查看存储过程和函数的信息

MySQL中存储过程和函数的信息存储在information_schema数据库下的 Routines表中。可以通过查询该表的记录来查询存储过程和函数的信息。其基本语法形式如下:

sql 复制代码
SELECT * FROM information_schema.Routines
WHERE ROUTINENAME = 'sp_name';

其中,ROUTINE_NAME字段中存储的是存储过程和函数的名称; sp_name参数表示存储过程或函数的名称。

【例26】从Routines表中查询名称为CountProc的存储函数的信息,代码如下:

sql 复制代码
SELECT * FROM information_schema.Routines
WHERE ROUTINE_NAME= 'NameBy2ip' AND ROUTINE_TYPE= 'FUNCTION';

代码执行结果如下:

在information_schema数据库下的Routines表中,存储所有存储过程和函数的定义。使用SELECT语句查询Routines表中的存储过程和函数的定义时,一定要使用ROUTINE_NAME字段指定存储过程或函数的名称。否则,将查询出所有的存储过程或函数的定义。如果有存储过程和存储函数名称相同,则需要同时指定ROUTINE_TYPE字段表明查询的是哪种类型的存储程序。

4,修改存储过程和函数

使用ALTER语句可以修改存储过程或函数的特性,本节将介绍如何使用ALTER语句修改存储过程和函数。

sql 复制代码
ALTER { PROCEDURE | FUNCTION} sp_name [characteristic ...]

其中,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语句。但是,这两个语句的结构是一样的,语句中的所有参数也是一样的。而且,它们与创建存储过程或函数的语句中的参数也是基本一样的。

【例27】修改存储过程CountProc的定义。将读写权限改为MODIFIES SQL DATA,并指明调用者可以执行,代码如下:

sql 复制代码
ALTER PROCEDURE CountProc
MODIFIES SQL DATA
SQL SECURITY INVOKER;

执行代码,并查看修改后的信息。结果显示如下:

sql 复制代码
-- 查询修改后的CountProc表信息
SELECT SPECIFIC_NAME, SQL_DATA_ACCESS,SECURITY_TYPE
FROM information_schema.Routines
WHERE ROUTINE_NAME='CountProc' AND ROUTINE_TYPE = 'PROCEDURE';

结果显示,存储过程修改成功。从查询的结果可以看出,访问数据的权限(SQL_DATA_ACCESS)已经变成 MODIFIES SQL DATA,安全类型(SECURITY_TYPE)已经变成了INVOKER。

【例28】修改存储函数CountProc的定义。将读写权限改为READS SQL DATA,并加上注释信息"FIND NAME",代码如下:

sql 复制代码
ALTER FUNCTION CountProc
READS SQL DATA
COMMENT 'FIND NAME';

执行代码,并查看修改后的信息。结果显示如下:

sql 复制代码
-- 查询修改后CountProc表的信息 
SELECT SPECIFIC_NAME, SQL_DATA_ACCESS, ROUTINE_COMMENT 
FROM information_schema.Routines 
WHERE ROUTINE_NAME='CountProc' AND ROUTINE_TYPE = 'FUNCTION';

存储函数修改成功。从查询的结果可以看出,访问数据的权限(SQL_DATA_ACCESS)已经变成READS SQL DATA,函数注释(ROUTINE_COMMENT)已经变成了FIND NAME。

5,删除存储过程和函数

删除存储过程和函数,可以使用DROP语句,其语法结构如下:

sql 复制代码
DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name

这个语句被用来移除一个存储过程或函数。sp_name为要移除的存储过程或函数的名称。IF EXISTS子句是一个MySQL 的扩展。如果程序或函数不存储,它可以防止发生错误,产生一个用SHOW WARNINGS查看的警告。

【例29】删除存储过程和存储函数,代码如下:

sql 复制代码
DROP PROCEDURE CountProc;
DROP FUNCTION CountProc;

上面语句的作用就是删除存储过程CountProc和存储函数CountProc。

6,疑问解答

疑问1 : MySQL存储过程和函数有什么区别?

在本质上它们都是存储程序。函数只能通过return 语句返回单个值或者表对象;而存储过程不允许执行return 语句,但是可以通过out参数返回多个值。函数限制比较多,不能用临时表,只能用表变量,还有一些函数都不可用等等;而存储过程的限制相对就比较少。函数可以嵌入在SQL语句中使用,可以在SELECT语句中作为查询语句的一个部分调用;而存储过程一般是作为一个独立的部分来执行。

疑问2∶存储过程中的代码可以改变吗?

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

疑问3∶存储过程中可以调用其他存储过程吗?

存储过程包含用户定义的SQL语句集合,可以使用CALL语句调用存储过程,当然在存储过程中也可以使用CALL语句调用其他存储过程,但是不能使用DROP语句删除其他存储过程。

疑问4:存储过程的参数不要与数据表中的字段名相同。

在定义存储过程参数列表时,应注意把参数名与数据库表中的字段名区别开来,否则将出现无法预期的结果。

疑问5∶存储过程的参数可以使用中文吗?

一般情况下,可能会出现存储过程中传入中文参数的情况,例如某个存储过程根据用户的名字查找该用户的信息,传入的参数值可能是中文。这时需要在定义存储过程的时候,在后面加上 character set gbk不然调用存储过程使用中文参数会出错,比如定义userInfo存储过程代码如下:

sql 复制代码
CREATE PROCEDURE useInfo(IN u_name VARCHAR (50) character set gbk, OUT u_age INT);

总结:

存储过程和函数,在使用方面,注意:DELIMITER // 分隔符的使用,分隔符可以自定义。其它就看业务处理逻辑了。

创建过程和函数:

CREATE PROCEDURE sp_name()

CREATE FUNCTION func_name()

调用过程和函数:

CALL sp_name();

SELECT func_name();

查看过程和函数的定义:

SHOW CREATE ROCEDURE sp_name;

SHOW CREATE FUNCTION func_name;

删除过程和函数:

DROP PROCEDURE sp_name;

DROP FUNCTION func_name;

上一篇: 《mysql 索引

下一篇: 《mysql 视图

相关推荐
hummhumm11 分钟前
Oracle 第29章:Oracle数据库未来展望
java·开发语言·数据库·python·sql·oracle·database
gavin_gxh36 分钟前
ORACLE 删除archivelog日志
数据库·oracle
一叶飘零_sweeeet39 分钟前
MongoDB 基础与应用
数据库·mongodb
猿小喵1 小时前
DBA之路,始于足下
数据库·dba
tyler_download1 小时前
golang 实现比特币内核:实现基于椭圆曲线的数字签名和验证
开发语言·数据库·golang
weixin_449310842 小时前
高效集成:聚水潭采购数据同步到MySQL
android·数据库·mysql
CodingBrother2 小时前
MySQL 和 PostgreSQL 的使用案例
mysql·adb·postgresql
Cachel wood2 小时前
Github配置ssh key原理及操作步骤
运维·开发语言·数据库·windows·postgresql·ssh·github
standxy2 小时前
如何将钉钉新收款单数据高效集成到MySQL
数据库·mysql·钉钉
Narutolxy4 小时前
MySQL 权限困境:从权限丢失到权限重生的完整解决方案20241108
数据库·mysql