【开发篇】MySQL的存储过程和函数的操作


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

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

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

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

1、创建

存储过程创建语法:

delimiter //
create procedure 储存名([in,out,inout]参数名 数据类形……)
begin
  sql语句
end
//
delimiter;

函数创建语法:

delimiter //
CREATE FUNCTION sp_name ([in]参数名 数据类形……])
RETURNS type
begin
  sql语句
end
//
delimiter;

-- 示例

DELIMITER //

CREATE PROCEDURE film_in_stock(IN p_film_id INT, IN p_store_id INT, OUT p_film_count INT)
READS SQL DATA
BEGIN
	SELECT inventory_id
	FROM inventory
	WHERE film_id = p_film_id
	AND store_id = p_store_id
	AND inventory_in_stock(inventory_id);
	
	SELECT FOUND_ROWS() INTO p_film_count;
END 
//
DELIMITER ;

  上面是在使用的样例数据库中创建的一个过程,该过程用来检查 film_id 和 store_id 对应的 inventory 是否满足要求,并且返回满足要求的 inventory_id 以及满足要求的记录数。

代码说明:

  • “DELIMITER //” 将语句的结束符从 “;” 变成 “//” ,也可以是你想要的任何符号,这样的目的是为了存储过程中的 “;” 不被当成结束语句而提示错误。
  • READS SQL DATA :表示子程序包含读数据的语句,但不包含写数据的语句。mysql创建存储过程的时候还有三个这样的数据存取限制的参数,分别是CONTAINS SQL(表示子程序不包含读或写数据的语句),NO SQL(子程序不包含SQL语句),MODIFIES SQL DATA(表示子程序包含写数据的语句),默认的是CONTAINS SQL。,这些特征值目前只是提供给服务器,并没有根据这些特征值来约束过程实际使用数据的情况。
  • 存储过程的参数:支持三种,in,out,inout ,三种参数的说明:in参数:默认参数形式,传入参数,该参数的值必须在调用存储过程事指定。out参数:传出参数,不能用于传入参数值,调用存储过程时,out参数也需要指定,但必须是变量,不能是常量。inout参数:可变变量,调用时可传入值,在调用过程中,可修改其值,同时也可返回值,调用时传入的是变量,而不是常量;
  • inventory_in_stock:这是存储过程调用其他的函数(或者存储过程)。

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

2、调用

函数和存储过程的调用方式一致,如下:
CALL sp_name([parameter[,…]])

-- 调用上面创建的存储过程

 CALL film_in_stock(2,2,@a)
 +--------------+
| inventory_id |
+--------------+
| 10 |
| 11 |
+--------------+
2 rows in set (0.00 sec)

-- 存储过程的输出型变量
 select @a;
 +------+
| @a |
+------+
| 2 |
+------+
1 row in set (0.00 sec)

3、查看

  存储过程或者函数创建后,用户可能需要查看存储过程或者函数的状态或者定义等信息,便于了解存储过程或者函数的基本情况。下面将介绍如何查看存储过程或函数相关信息。

1.状态查看

语法:
SHOW {PROCEDURE | FUNCTION} STATUS [LIKE ‘pattern’]

-- 示例:
show procedure status like 'film_in_stock;

*************************** 1. row ***************************
 Db: sakila
 Name: film_in_stock
 Type: PROCEDURE
 Definer: root@localhost
 Modified: 2007-07-06 09:29:00
 Created: 2007-07-06 09:29:00
Security_type: DEFINER
 Comment: 
1 row in set (0.00 sec)

2.定义查看

语法:
SHOW CREATE {PROCEDURE | FUNCTION} sp_name

-- 示例:

show create procedure film_in_stock;

*************************** 1. row ***************************
 Procedure: film_in_stock
 sql_mode: 
Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE `film_in_stock`(IN p_film_id 
INT, IN p_store_id INT, OUT p_film_count INT)
 READS SQL DATA
BEGIN
 SELECT inventory_id
 FROM inventory
 WHERE film_id = p_film_id
 AND store_id = p_store_id
 AND inventory_in_stock(inventory_id);
 SELECT FOUND_ROWS() INTO p_film_count;
END
1 row in set (0.00 sec)
 

3.信息查看

  除了以上两种方法,我们还可以查看系统表来了解存储过程和函数的相关信息,通过查看information_schema. Routines 就可以获得存储过程和函数的包括名称、类型、语法、创建人等信息。

-- 例如,通过查看 information_schema. Routines 得到过程 film_in_stock 的定义:

select * from routines where ROUTINE_NAME = 'film_in_stock';

4、删除

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

删除存储过程或者函数的语法:
DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name

-- 示例:删除存储过程film_in_stock

DROP PROCEDURE film_in_stock;

5、变量的使用

  存储过程和函数中可以使用变量,而且在 MySQL 5.1 版本之后,变量是不区分大小写的。

1.变量的定义

  通过 DECLARE 可以定义一个局部变量,该变量的作用范围只能在 BEGIN…END 块中,可以用在嵌套的块中。变量的定义必须写在复合语句的开头,并且在任何其他语句的前面。可以一次声明多个相同类型的变量。如果需要,可以使用 DEFAULT 赋默认值。

语法:
DECLARE var_name[,…] type [DEFAULT value]

-- 示例1:定义date类型的变量last_month_start 

DECLARE last_month_start DATE;

-- 示例2:一次定义多个同类型变量,并指定默认值

DECLARE count_id,count_name int DEFAULT 0;

2.变量的赋值

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

语法:
SET var_name = expr [, var_name = expr] …

-- 示例:
SET last_month_start = '1999-12-31';
SET last_month_start = DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH);

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

语法:
SELECT col_name[,…] INTO var_name[,…] table_expr

DELIMITER //
CREATE FUNCTION get_customer_balance(p_customer_id INT, p_effective_date DATETIME) 
RETURNS DECIMAL(5,2)
DETERMINISTIC
READS SQL DATA
BEGIN
	 DECLARE v_payments DECIMAL(5,2); #SUM OF PAYMENTS MADE PREVIOUSLY
	 
	 SELECT IFNULL(SUM(payment.amount),0) INTO v_payments
	 FROM payment
	 WHERE payment.payment_date <= p_effective_date
	 AND payment.customer_id = p_customer_id;

	 RETURN v_rentfees + v_overfees - v_payments;
END 
//

DELIMITER ;

3.MySQL中的其他变量

  上面讲解的变量叫局部变量,也就是说,只能在函数或者存储过程中使用。而MySQL还有两个两个变量。分别是用户变量和全局变量。

用户变量:

语法:
@var_name    不需要提前声明,使用即声明

  它的使用范围也可以算是在函数或者存储过程中,但有个问题是滥用用户变量会导致程序难以理解及管理

全局变量:

语法:
@@global.var_name

  由系统系统,在整个数据库中有效。

6、条件和处理

  在程序的运行过程中可能会遇到问题,此时我们可以通过定义条件和处理程序来事先定义这些问题。定义条件是指事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题时应当采取的处理方式和解决办法,保证存储过程和函数在遇到警告或错误时能继续执行,从而增强程序处理问题的能力,避免程序出现异常被停止执行,相当于python中的try…except。

1.条件的定义

语法:

DECLARE condition_name CONDITION FOR condition_value

condition_value:
SQLSTATE [VALUE] sqlstate_value| mysql_error_code

参数说明:

cond_name:条件名称
condition_value:条件类型,用于定义MySQL的错误,SQLSTATE是长度为5的字符串类型的错误代码;mysql_error_code是数值类型的错误代码。
例如 ERROR 1142(42000)
sql_state_value的值是’42000’,mysql_error_code的值是1142。

--示例:对错误ERROR 1148(42000),定义条件如下:

-- 使用SQLSTATE
DECLARE cond_error CONDITION FOR SQLSTATE '42000';
-- 使用mysql_error_code
DECLARE cond_error CONDITION FOR 1148;

2.条件的处理

条件处理的语法:
DECLARE handler_type HANDLER FOR condition_value[,…] sp_statement


1、handler_type:指定错误处理方式
  CONTINUE:遇到错误不处理,继续执行
  EXIT:遇到错误立即退出
   UNDO:遇到错误撤回之前的操作

2、condition_value:表示错误类型
   SQLSTATE [VALUE] sqlstate_value:包含5个字符的字符串错误值
   condition_name:定义条件的名称,就是前面定义的条件的名称
   SQLWARNING:匹配所有以01开头的SQLSTATE错误代码
   NOT FOUND:匹配所有以02开头的SQLSTATE错误代码
   SQL EXCEPTION:匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码
   mysql_error_code:包含5个字符的字符串错误值

3、sp_statement:程序语句段,表示在遇到定义的错误时执行的存储过程或函数。

  下面将通过两个例子来说明:在向 actor 表中插入记录时,如果没有进行条件的处理,那么在主键重的时候会抛出异常并退出,如果对条件进行了处理,那么就不会再抛出异常。顺便理解前面讲解的各种东西的使用。

-- 当不设置条件处理时
delimiter $$

CREATE PROCEDURE actor_insert ()
BEGIN
	SET @x = 1;
	INSERT INTO actor(actor_id,first_name,last_name) VALUES (201,'Test','201');
	SET @x = 2;
	INSERT INTO actor(actor_id,first_name,last_name) VALUES (1,'Test','1');
	SET @x = 3;
END
$$
delimiter ;

--调用上面定义的存储过程
call actor_insert();

ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

--通过定义的用户变量检查问题出在哪里
mysql> select @x;
+------+
| @x |
+------+
| 2 |
+------+
1 row in set (0.00 sec)

  从上面的例子可以看出,执行到插入 actor_id=1 的记录时,会主键重复并退出,没有执行到下面其他的语句。

-- 设置条件处理
delimiter $$

CREATE PROCEDURE actor_insert ()
BEGIN
	declare continue HANDLER FOR SQLSTATE '23000' set @x2 = 1;
	SET @x = 1;
	INSERT INTO actor(actor_id,first_name,last_name) VALUES (201,'Test','201');
	SET @x = 2;
	INSERT INTO actor(actor_id,first_name,last_name) VALUES (1,'Test','1');
	SET @x = 3;
END
$$
delimiter ;

--调用上面定义的存储过程
call actor_insert();
Query OK, 0 rows affected (0.06 sec)

--通过定义的用户变量检查判断存储过程执行的情况
select @x,@x2;
+------+------+
| @x | @x2 |
+------+------+
| 3 | 1 |
+------+------+
1 row in set (0.00 sec)

  从上面的例子的用户变量检查可以看出,虽然执行到插入 actor_id=1 的记录时,主键重复出现异常,但由于定义了条件处理(例子中时continue ,遇到错误继续执行),所以 用户变量@x,@x2的值分别是3和1。

7、光标的使用

  查询语句时,可能查询出多条记录,在存储过程和函数中使用光标来逐条读取查询结果集中的记录。有些书上将光标称为游标。光标的使用包括声明光标(DECLARE)、打开光标(OPEN)、使用光标(FETCH)和关闭光标(CLOSE)。光标必须声明在处理程序之前,并且声明在变量和条件之后。光标可以类比为数组内部中的循环指针。其语法分别如下。

1、声明光标:
DECLARE cursor_name CURSOR FOR select_statement;

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

示例:
DECLARE cur_student CURSOR FOR SELECT id, name FROM student;


2、OPEN 光标:
OPEN cursor_name

3、FETCH 光标:
FETCH cursor_name INTO var_name [, var_name] …

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


4、CLOSE 光标:关闭之后就不能使用FETCH来使用光标了。
CLOSE cursor_name

光标使用示例:
 现有表,形如:
在这里插入图片描述

-- 创建存储过程
delimiter //

create Procedure student_info() -- 无参存储过程
READS SQL DATA
begin
	declare stu_id int;
	declare stu_name varchar(200);

	declare cur_student cursor for select id,name from students; -- 定义游标
	declare exit handler for not found  close  cur_student;-- 定义条件处理:找不到时关闭游标退出
	
	set @id_ji = '';
	set @id_ou = '';
	set @name_ji = '';
	set @name_ou = '';

	open cur_student;--打开游标
	REPEAT
	    FETCH cur_student INTO stu_id,stu_name;-- 使用游标
	    IF stu_id%2=1 THEN
		    SET  @id_ji = @id_ji + stu_id;
			SET  @name_ji =CONCAT(@name_ji,' ',stu_name);
	    ELSE
		    SET  @id_ou = @id_ou + stu_id;
			SET  @name_ou =CONCAT(@name_ou,' ',stu_name);
	    END IF;
	UNTIL 0 END REPEAT;
	
CLOSE cur_student;-- 关闭游标
		END 
		//
DELIMITER ;	

-- 调用
CALL student_info();

-- 结果查看
SELECT @id_ji,@id_ou,@name_ji,@name_ou;

 结果输出,形如:
在这里插入图片描述

注意:
  变量、条件、条件处理、光标都是通过 DECLARE 定义的,它们之间是有先后顺序的要求的。变量和条件必须在最前面声明,然后才能是光标的声明,最后才可以是条件处理的声明。

8、流程控制

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

1.if语句

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

语法:
IF search_condition THEN statement_list
  [ELSEIF search_condition THEN statement_list] …
  [ELSE statement_list]
END IF

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

  MySQL中还有一个 IF() 函数,它不同于 IF 语句。

2.case语句

  CASE 实现比 IF 更复杂一些的条件构造,具体语法如下:

语法:
CASE case_value
  WHEN when_value THEN statement_list
  [WHEN when_value THEN statement_list] …
  [ELSE statement_list]
END CASE
  
Or:
  
CASE
  WHEN search_condition THEN statement_list
  [WHEN search_condition THEN statement_list] …
  [ELSE statement_list]
END CASE

示例:上面光标的示例,case语句实现

case
	when stu_id%2=1 THEN
		    SET  @id_ji = @id_ji + stu_id;
			SET  @name_ji =CONCAT(@name_ji,' ',stu_name);
	ELSE
		SET  @id_ou = @id_ou + stu_id;
		SET  @name_ou =CONCAT(@name_ou,' ',stu_name);
END case;

--或者
case mod(stu_id,2)
	when 1 THEN
	    SET  @id_ji = @id_ji + stu_id;
		SET  @name_ji =CONCAT(@name_ji,' ',stu_name);
	ELSE
		SET  @id_ou = @id_ou + stu_id;
		SET  @name_ou =CONCAT(@name_ou,' ',stu_name);
END case;

3.loop语句

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

语法:
[begin_label:] LOOP
  statement_list
END LOOP [end_label]

示例:

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;

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

4.leave语句

  leave语句用来从标注的流程构造中退出,通常和 BEGIN … END 或者循环一起使用。语法如下:

语法:其中label 参数表示循环的标志
LEAVE lable

  示例见loop循环示例。

5.iterate语句

  ITERATE 语句必须用在循环中,作用是跳过当前循环的剩下的语句,直接进入下一轮循环。类似python中break。

示例:

delimiter //

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
//

delimiter ;

6.repeat语句

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

语法:
[begin_label:] REPEAT
  statement_list
UNTIL search_condition
END REPEAT [end_label]

示例:

-- 例子1:
REPEAT
	    FETCH cur_student INTO stu_id,stu_name;-- 使用游标
	    IF stu_id%2=1 THEN
		    SET  @id_ji = @id_ji + stu_id;
			SET  @name_ji =CONCAT(@name_ji,' ',stu_name);
	    ELSE
		    SET  @id_ou = @id_ou + stu_id;
			SET  @name_ou =CONCAT(@name_ou,' ',stu_name);
	    END IF;
	UNTIL 0 
END REPEAT;

--例子2 id 值小于10, 将重复执行循环过程
DECLARE id INT DEFAULT 0;
REPEAT
	SET id = id + 1;
UNTIL  id >= 10
END REPEAT;

7.while语句

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

语法:
[begin_label:] WHILE search_condition DO
  statement_list
END WHILE [end_label]

示例: id 值小于10, 将重复执行循环过程


DECLARE i INT DEFAULT 0;
WHILE i < 10 DO
	SET i = i + 1;
END WHILE;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值