存储过程是一条或者多条SQL语句的集合,相当于批处理文件,但是作用不仅仅限于批处理。使用存储过程将简化操作,减少冗余的步骤,同时还可减少操作过程的失误,提高效率。
(1)创建存储过程
创建存储过程是通过”CREATE PROCEDURE”语句来创建,语法格式为:
CREATE PROCEDURE sp_name ([proc_parameter])
[characteristics…] routine_body
各选项说明:
CREATE PROCEDURE:创件存储过程的关键字
sp_name:存储过程名称
proc_parameter:指定存储过程的参数列表,列表形式为:[ IN | OUT | INOUT ] param_name type
characteristics:用于指定存储过程的特性
routine_body:mysql sql语句内容,使用BEGINE…END来表示SQL代码的开始和结束。
# 创建存储过程前先将sql语句结束符改为//,以防止和默认的结束符冲突,
mysql> delimiter //
mysql> create procedure proc()
-> begin
-> select * from course;
-> end //
Query OK, 0 rows affected (0.08 sec)
# 存储过程创建完成后将结束符改为默认的结束符
mysql> delimiter ;
# 调用创建的存储过程
mysql> call proc();
+----+-------------+------------+
| id | course_name | teacher_id |
+----+-------------+------------+
| 1 | math | 3 |
| 2 | english | 2 |
(2)创建存储函数
函数与存储过程最大的区别就是函数调用有返回值,调用存储过程用call语句,而调用函数就直接引用函数名+参数即可,创建存储函数使用”CREATE FUNCTION”语句来创建,语法格式为:
CREATE FUNCTION func_name ( param_name type )
RESTURNS type
[characteristic……] routine_body
主要参数说明:
Param_name:参数名称
Type:参数类型
RETURNS type:函数返回数据的类型
Characteristic:指定存储函数的特性
mysql> delimiter //
# 创建一个函数
mysql> create function stubak_update(param1 int)
-> returns int
-> begin
-> update student_bak set gender=1 where sid=param1;
-> select count(*) into @a from student_bak where sid>param1;
-> return @a;
-> end;
-> //
mysql> delimiter ;
# 调用函数,并传入一个参数
mysql> select stubak_update(1);
+------------------+
| stubak_update(1) |
+------------------+
| 7 |
+------------------+
(1)定义变量
在存储过程中通过DECLARE语句定义变量,定义变量的格式如下:
DECLARE var_name[,varname]… date_type [DEFAULT value];
选项说明:
var_name:局部变量名称
DEFAULT value:用于给变量提供一个默认值
Type:用于指定变量的默认类型
(2)为变量赋值
声明后的变量可以通过select … into var_list进行赋值,或者通过set语句赋值,或者通过定义游标并使用fetch … into var_list赋值,使用set赋值的方式如下:
SET var_name = expr [, var_name = expr] ……;
# mysql存储过程中使用变量
mysql> delimiter //
mysql> create procedure sp1(v_sid int)
-> begin
-> declare xname varchar(10) default 'dayi123';
-> declare xsex int;
-> select sname,gender into xname,xsex from student_bak where sid=v_sid;
-> select xname,xsex;
-> end;
-> //
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> call sp1(1);
+--------+------+
| xname | xsex |
+--------+------+
| Andrew | 1 |
+--------+------+
定义条件是事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题是应当采取的处理方式,并且保证存储过程或函数在遇到警告或错误时能继续执行。
(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为数值类型的错误代码
(2)定义处理程序
定义处理程序时,使用DECLARE语句实现:
DECLARE handler_type HANDLER FOR condition_value[,…] sp_statement handler_type:
CONTINUT | EXIT | UNDO
Condition_value:
SQLSTATE [VALUE] sqlstate_value | condition_name
| SQLWARNING | NOT FOUND | SQLEXCEPTION | mysql_error_code
各选项说明:
Handler_type:错误处理方式,continue表示遇到错误不处理,exit为退出,undo为撤回之前操作。
Condition_value表示错误类型,有以下的取值
SQLSTATE [VALUE] sqlstate_value:包含5个字符的字符串错误值
condition_name:declare condition定义错误的错误条件名称
SQLWARNING:匹配所有以01开头SQLSTATE错误代码
NOT FOUND:匹配所有以02开头的SQLSTATE错误代码
SQLEXCEPTION:匹配所有没有被SQLWARENING或NOT FOUNT捕获的SQLSTATE的错误代码
mysql_error_code:匹配数值类型的错误代码
sp_statement:在遇到定义的错误时,需要执行的存储过程或函数。
当condition发生但没有声明handler时,则存储过程和函数依照如下规则处理:
发生SQLEXCEPTION错误,则执行exit退出
发生SQLWARNING警告,则执行contine继续执行
发生NOT FOUND情况,则执行continue继续执行
# 创建一个存储过程并定义处理程序当错误吗为23000时跳过继续执行
# 创建一张用于测试的表
mysql> delimiter //
mysql> create procedure handlerdemo()
-> begin
-> declare continue handler for sqlstate '23000' set @x2=1;
-> set @x=1;
-> insert into t values(1);
-> set @x=2;
-> insert into t values(1);
-> set @x=3;
-> end;
-> //
mysql> delimiter ;
# 调用存储过程
mysql> CALL handlerdemo();
# 插看变量的值
mysql> select @x;
+------+
| @x |
+------+
| 3 |
+------+
MySQL支持if,case,iterate,leave,loop,while,repeat语句作为存储过程和函数中的流程控制语句,另外return语句也是函数中的特定流程控制语句。
(1)case语句
Case语句有两种语句格式分别如下:
格式一:
CASE case_value
WHEN when_value THEN statement_list
[WHEN when_value THEN statement_list]……
[ELSE statement_list]
END CASE
格式二:
CASH
WHEN search_condition THEN statement_list
[WHEN search_condition THEN statement_list]……
[ELSE statement_list]
END CASE
创建存储过程时使用case语句
mysql> delimiter //
mysql> CREATE PROCEDURE exp_case(v_sid int)
-> BEGIN
-> DECLARE v INT DEFAULT 1;
-> select gender into v from student_bak where sid=v_sid;
-> CASE
-> WHEN v=0 THEN update student_bak set gender=1 where sid=v_sid;
-> WHEN v=1 THEN update student_bak set gender=0 where sid=v_sid;
-> ELSE
-> update student_bak set gender=-1 where sid=v_sid;
-> END CASE;
-> END;
-> //
mysql> delimiter ;
(2)if语句
If语句包含多个条件判断,根据条件判断的结果为TRUE或FALSE执行相应的语句,语法格式为:
IF search_condition THEN statement_List
[ELSEIF search_condition THEN statement_list] …
[ELSE statement_list]
END IF
创建一个函数,使用if语句判断两个数的大小
mysql> CREATE FUNCTION exp_if(n INT, m INT)
-> RETURNS VARCHAR(20)
-> BEGIN
-> DECLARE s VARCHAR(20);
-> IF n > m THEN SET s = '>';
-> ELSEIF n = m THEN SET s = '=';
-> ELSE SET s = '<';
-> END IF;
-> SET s = CONCAT(n, ' ', s, ' ', m);
-> RETURN s;
-> END //
mysql> DELIMITER ;
mysql> select exp_if(1,2);
+-------------+
| exp_if(1,2) |
+-------------+
| 1 < 2 |
+-------------+
If语句也可在创建存储过程及函数时嵌套使用,
mysql> CREATE FUNCTION exp_if02 (n INT, m INT)
-> RETURNS VARCHAR(50)
-> BEGIN
-> DECLARE s VARCHAR(50);
-> IF n = m THEN SET s = 'equals';
-> ELSE
-> IF n > m THEN SET s = 'greater';
-> ELSE SET s = 'less';
-> END IF;
-> SET s = CONCAT('is ', s, ' than');
-> END IF;
-> SET s = CONCAT(n, ' ', s, ' ', m, '.');
-> RETURN s;
-> END //
mysql> delimiter ;
(3)iterate语句
Iterate语句仅出现在loop,repeat,while循环语句中,其含义表示重新开始此循环,格式如下:
ITERATE label
Label表示自定义的标签名
(4)leave语句
Leave语句表明退出指定标签的流程控制语句块,通常会用在begin…end,以及loop,repeat,while的循环语句中,格式如下:
LEAVE label
Label表示要退出的标签名
(5)LOOP语句
Loop语句是存储过程或函数中表达循环执行的一种方式,LOOP内的语句一直重复执行直到循环被退出,跳出循环时使用LEVAVE子句,LOOP语句的基本格式如下:
[begin_label:] LOOP
Statement_list
END LOOP [ END_LABEL ]
创建存储过程使用loop循环语句实现变量的自增
mysql> DELIMITER //
# 创建存储过程
mysql> CREATE PROCEDURE exp_loop(p1 INT)
-> BEGIN
-> label1: LOOP
-> SET p1 = p1 + 1;
-> IF p1 < 10 THEN
-> ITERATE label1;
-> END IF;
-> LEAVE label1;
-> END LOOP label1;
-> SET @x = p1;
-> END //
mysql> DELIMITER ;
# 调用存储过程
mysql> call exp_loop(1);
# 查看执行后变量的值
mysql> select @x;
+------+
| @x |
+------+
| 10 |
+------+
(6)repeat语句
Repeat语句用于创建一个带条件判断的循环过程,每次语句执行完毕之后,会对条件表达式进行判断,如果表达式为真,则循环结束;否则重复执行循环中的语句。
[repeat_label:] REPEAT
Statement_List
UNTIL search_condition
END REPEAT [repeat_label]
Repeat_label为REPEAT语句的标注名称,该参数可省略
mysql> delimiter //
# 创建基于repeat循环的存储过程
mysql> CREATE PROCEDURE exp_rep(p1 INT)
-> BEGIN
-> SET @x = 0;
-> REPEAT
-> SET @x = @x + 1;
-> UNTIL @x > p1 END REPEAT;
-> END
-> //
mysql> delimiter ;
# 调用存储过程,并查看变量@x的最终值
mysql> call exp_rep(100);
mysql> select @x;
+------+
| @x |
+------+
| 101 |
+------+
(7)while语句
While语句也是用于创建一个带条件判断的存储过程,与REPEAT不同的是while在执行语句时先对指定的表达式进行判断,如果为真,则执行循环内的语句,否则退出,语句格式如下:
[while_label:] WHILE expr_condition DO
Statement_list
END WHILE [while_label]
创建基于while循环的存储过程,相对于repeat循环是先判断在执行。
mysql> DELIMITER //
mysql> CREATE PROCEDURE exp_whi(p1 INT)
-> BEGIN
-> SET @b = 0;
-> WHILE @b < p1 DO
-> SET @b = @b + 1;
-> END WHILE;
-> END;
-> //
mysql> DELIMITER ;
mysql> call exp_whi(100);
mysql> select @b;
+------+
| @b |
+------+
| 100 |
+------+
Mysql查询语句可能返回多条记录,如果数据量大则需要在存储过程中和储存函数中使用游标来逐条读取查询结果中的记录。应用程序可以根据需要滚动或浏览其中的数据。
(1)声明游标
游标必须在声明处理程序之前被声明,并且变量和条件必须在声明游标或处理程序之前被声明。声明游标的语句如下:
DECLARE cursor_name CURSOR FOR select_statement
(2)打开游标
打开游标的语句如下:
OPEN cursor_name
(3)使用游标
使用游标语句如下
FETCH cursor_name INTO var_name [,var_name] ……
var_name:表示将光标中的SELECT 语句查询出来的信息存入该参数中,var_name必须在声明光标之前就定义好。
使用游标时,数据集中的字段需要和INTO语句中定义的变量一一对应,数据集中的数据都fetch完之后,则返回NOT FOUND。
(4)关闭游标
关闭游标语句如下:
CLOSE cursor_name
(5)游标的使用
mysql> DELIMITER //
mysql> CREATE PROCEDURE exp_cur()
-> BEGIN
-> DECLARE done INT DEFAULT FALSE;
-> DECLARE a CHAR(16);
-> DECLARE b, c INT;
-> DECLARE cur1 CURSOR FOR SELECT sname,dept_id FROM student;
-> DECLARE cur2 CURSOR FOR SELECT id FROM course;
-> DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-> OPEN cur1;
-> OPEN cur2;
-> read_loop: LOOP
-> FETCH cur1 INTO a, b;
-> FETCH cur2 INTO c;
-> IF done THEN
-> LEAVE read_loop;
-> END IF;
-> IF b = c THEN
-> INSERT INTO test VALUES (a,b);
-> ELSE
-> INSERT INTO test VALUES (a,c);
-> END IF;
-> END LOOP;
-> CLOSE cur1;
-> CLOSE cur2;
-> END //
mysql> DELIMITER ;
在创建和存储过程和函数后可以通过”show status”语句或”show create”语句来查看创建的存储过程和函数,也可直接从系统的”information_schema”数据库中查询。
使用”show status”语句查看存储过程和函数状态语句结构如下:
SHOW {PROCEDURE | FUNCTION} STATUS [LIKE ‘pattern’]
# 查看创建的所有以”exp”开头的存储过程
mysql> show procedure status like 'exp%' \G
*************************** 1. row ***************************
Db: course
Name: exp_case
Type: PROCEDURE
Definer: root@localhost
……
使用”show create”查看存储和函数状态语句如下:
SHOW CREATE {PROCEDURE | FUNCTION} sp_name
从”information_schema.routines”表中查看存储过程和函数信息的语句结构如下:
Select * from information_schema.routines where routing_name=’sp_name’;
存储过程及函数穿件完成后可以通过”alter”语句来修改存储过程或函数的特性,修改的语句如下:
ALTER {PROCEDURE|FUNCTION} sp_name [characteristic …]
Characteristic参数用于指定存储函数的特性,取值有:
CONTAINS SQL:表示子程序包含SQL语句,但不包含读或写数据的语句。
NO SQL:表示子程序中不包含SQL语句
READS SQL DATA:表示子程序中包含读数据的语句
MODIFIES SQL DATA:表示子程序中包含写数据的语句
SQL SECURITY {DEFINER|INVOKER}:指明谁有权限来执行
DEFINER:表示只有定义者自己才能够执行
INVOKER:表名调用者可以执行
COMMENT ‘string’:表示注释信息
删除存储过程及存储函数使用”drop”语句来删除,语法格式如下:
DROP {PROCEDURE|FUNCTION} [IF EXISTS] sp_name
删除一个创建的存储过程:
mysql> drop procedure exp_whi;
Query OK, 0 rows affected (0.00 sec)
触发器是一个特殊的存储过程,触发器的作用是当表上有对应SQL语句发生时,则触发执行。
(1)创建触发器
创建触发器的语句如下:
CREATE
[DEFINER = {user | CURRENT_USER }]
TRIGGER trigger_name
Trigger_time trigger_event
ON tbl_name FOR EACH ROW
[trigger_order]
trigger_body
各选项说明:
Definer:用来指定trigger的安全环境
trigger_name:标识触发器的名称
Trigger_time:指定触发器的执行时间,BEFORE代表在数据修改前执行,AFTER代表在修改后执行。
Trigger_event:指定触发该触发器的具体事件,INSERT当新的一行数据插入表中时触发,UPDATE当表的一行数据被修改时触发,DELETE当表的一行数据被删除时触发,当执行insert into … on duplicate key update语句时,当碰到重复行执行update时,则触发update下的触发器
tbl_name:标识建立触发器的表名
Trigger_body:表示触发器触发之后要执行的一个或多个语句,在内部可以引用涉及表的字段,OLD.col_name表示行数据被修改或删除之前的字段数据,NEW.col_name表示行数据被插入或修改之后的字段数据
# 先创建一张表用于在触发触发器时备份要修改的数据
mysql> create table student_back(sid int,old_sname varchar(12),sname varchar(12),old_gender int,gender int,update_time time)//
mysql> delimiter //
# 创建触发器
mysql> create trigger update_trigger
-> after update
-> on student for each row
-> begin
-> insert into student_back values(old.sid,old.sname,new.sname,old.gender,new.gender,now());
-> end;
-> //
mysql> delimiter ;
mysql> update student set gender=1 where sid=1;
# 更新数据后查看备份的数据
mysql> select * from student_back;
+------+-----------+--------+------------+--------+-------------+
| sid | old_sname | sname | old_gender | gender | update_time |
+------+-----------+--------+------------+--------+-------------+
| 1 | Andrew | Andrew | 0 | 1 | 19:12:29 |
+------+-----------+--------+------------+--------+-------------+
(2)查看触发器
触发器创建好后可以通过”show triggers”命令查看,也可在”triggers”表中查看触发器信息。
通过命令查看:show triggers;
通过表查看语句:select * from information_schema.triggers where trigger_name=' trigger_name ' \G
(3)删除触发器
触发器可以通过”drop trigger”语句来删除,删除触发器的语句格式为:
DROP TRIGGER [schema_name.] trigger_name
schema_name表示数据库的名称为可选参数
# 删除触发器
mysql> drop trigger update_trigger;
Query OK, 0 rows affected (0.00 sec)