MySQL存储过程简介 | ||||||||||||||||
我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。 一个存储过程是一个可编程的函数,它在数据库中创建并保存。它可以有SQL语句和一些特殊的控制结构组成。当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的。数据库中的存储过程可以看做是对编程中面向对象方法的模拟。它允许控制数据的访问方式。 存储过程通常有以下优点: (1).存储过程增强了SQL语言的功能和灵活性。存储过程可以用流控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。 (2).存储过程允许标准组件是编程。存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句。而且数据库专业人员可以随时对存储过程进行修改,对应用程序源代码毫无影响。 (3).存储过程能实现较快的执行速度。如果某一操作包含大量的Transaction-SQL代码或分别被多次执行,那么存储过程要比批处理的执行速度快很多。因为存储过程是预编译的。在首次运行一个存储过程时查询,优化器对其进行分析优化,并且给出最终被存储在系统表中的执行计划。而批处理的Transaction-SQL语句在每次运行时都要进行编译和优化,速度相对要慢一些。 (4).存储过程能过减少网络流量。针对同一个数据库对象的操作(如查询、修改),如果这一操作所涉及的Transaction-SQL语句被组织程存储过程,那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,从而大大增加了网络流量并降低了网络负载。 (5).存储过程可被作为一种安全机制来充分利用。系统管理员通过执行某一存储过程的权限进行限制,能够实现对相应的数据的访问权限的限制,避免了非授权用户对数据的访问,保证了数据的安全。 2. 关于MySQL的存储过程 存储过程是数据库存储的一个重要的功能,但是MySQL在5.0以前并不支持存储过程,这使得MySQL在应用上大打折扣。好在MySQL 5.0终于开始已经支持存储过程,这样即可以大大提高数据库的处理速度,同时也可以提高数据库编程的灵活性。 3. MySQL存储过程的创建 (1). 格式 MySQL存储过程创建的格式:CREATE PROCEDURE 过程名 ([过程参数[,...]]) procedure [特性 ...] 过程体 这里先举个例子: 1. mysql> DELIMITER // 2. mysql> CREATE PROCEDURE proc1(OUT s int) 3. -> BEGIN 4. -> SELECT COUNT(*) INTO s FROM user; 5. -> END 6. -> // 7. mysql> DELIMITER ; 注: (1)这里需要注意的是DELIMITER //和DELIMITER ;两句,DELIMITER是分割符的意思,因为MySQL默认以";"为分隔符,如果我们没有声明分割符,那么编译器会把存储过程当成SQL语句进行处理,则存储过程的编译过程会报错,所以要事先用DELIMITER关键字申明当前段分隔符,这样MySQL才会将";"当做存储过程中的代码,不会执行这些代码,用完了之后要把分隔符还原。 (2)存储过程根据需要可能会有输入、输出、输入输出参数,这里有一个输出参数s,类型是int型,如果有多个参数用","分割开。 (3)过程体的开始与结束使用BEGIN与END进行标识。 这样,我们的一个MySQL存储过程就完成了,是不是很容易呢?看不懂也没关系,接下来,我们详细的讲解。 (2). 声明分割符 delimiter 分隔符 delimiter // 其实,关于声明分割符,上面的注解已经写得很清楚,不需要多说,只是稍微要注意一点的是:如果是用MySQL的Administrator管理工具时,可以直接创建,不再需要声明。 (3). 参数 MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT,形式如: CREATE PROCEDURE([[IN |OUT |INOUT ] 参数名 数据类形...]) IN 输入参数:表示该参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值 OUT 输出参数:该值可在存储过程内部被改变,并可返回 INOUT 输入输出参数:调用时指定,并且可被改变和返回 ?. IN参数例子 创建: mysql > DELIMITER // 1. 2. mysql > CREATE PROCEDURE demo_in_parameter(IN p_in i nt) 3. -> BEGIN 4. -> SELECT p_in; /*查询输入参数*/ 5. -> SET p_in=2; /*修改*/ 6. -> SELECT p_in; /*查看修改后的值*/ 7. -> END; 8. -> // 9. mysql > DELIMITER ; 执行结果: 1. mysql > SET @p_in=1; 2. mysql > CALL demo_in_parameter(@p_in); 3. +------+ 4. | p_in | 5. +------+ 6. | 1 | 7. +------+ 8. 9. +------+ 10. | p_in | 11. +------+ 12. | 2 | 13. +------+ 14. 15. mysql> SELECT @p_in; 16. +-------+ 17. | @p_in | 18. +-------+ 19. | 1 | 20. +-------+ 以上可以看出,p_in虽然在存储过程中被修改,但并不影响@p_id的 值 ?.OUT参数例子 创建: 1. mysql > DELIMITER // 2. mysql > CREATE PROCEDURE demo_out_parameter(OUT p_ou t int) 3. -> BEGIN 4. -> SELECT p_out;/*查看输出参数*/ 5. -> SET p_out=2;/*修改参数值*/ 6. -> SELECT p_out;/*看看有否变化*/ 7. -> END; 8. -> // 9. mysql > DELIMITER ; 执行结果: 1. mysql > SET @p_out=1; 2. mysql > CALL sp_demo_out_parameter(@p_out); 3. +-------+ 4. | p_out | 5. +-------+ 6. | NULL | 7. +-------+ 8. /*未被定义,返回NULL*/ 9. +-------+ 10. | p_out | 11. +-------+ 12. | 2 | 13. +-------+ 14. 15. mysql> SELECT @p_out; 16. +-------+ 17. | p_out | 18. +-------+ 19. | 2 | 20. +-------+ ?. INOUT参数例子 创建: 1. mysql > DELIMITER // 2. mysql > CREATE PROCEDURE demo_inout_parameter(INOUT p_inout int) 3. -> BEGIN 4. -> SELECT p_inout; 5. -> SET p_inout=2; 6. -> SELECT p_inout; 7. -> END; 8. -> // 9. mysql > DELIMITER ; 执行结果: 1. mysql > SET @p_inout=1; 2. mysql > CALL demo_inout_parameter(@p_inout) ; 3. +---------+ 4. | p_inout | 5. +---------+ 6. | 1 | 7. +---------+ 8. 9. +---------+ 10. | p_inout | 11. +---------+ 12. | 2 | 13. +---------+ 14. 15. mysql > SELECT @p_inout; 16. +----------+ 17. | @p_inout | 18. +----------+ 19. | 2 | 20. +----------+ (4). 变量 ?. 变量定义 DECLARE variable_name [,variable_name...] datatype [DEFAULT value]; 其中,datatype为MySQL的数据类型,如:int, float, date, varchar(length) 例如: unsigned 无符号 整型的每一种都有无符号(unsigned)和有符号(signed)两种类型(float和double总是带符号的),在默认情况下声明的整型变量都是有符号的类型(char有点特别),如果需声明无符号类型的话就需要在类型前加上unsigned。无符号版本和有符号版本的区别就是无符号类型能保存2倍于有符号类型的数据,比如16位系统中一个int能存储的数据的范围为-32768~32767,而unsigned能存储的数据范围则是0~65535。由于在计算机中,整数是以补码形式存放的。根据最高位的不同,如果是1,有符号数的话就是负数;如果是无符号数,则都解释为正数。同时在相同位数 的情况下,所能表达的整数范围变大。另外,unsigned若省略后一 个关键字,大多数编译器都会认为是unsigned int。 在sql语句中的意义 sql语句中,创建一个数据表时 ceate table user{ user_id int unsigned... ... } 当中的unsigned表示,数据项user_id恒为正整数。 declare 变量名称 变量的数据类型 约束条件; 1. DECLARE l_int int unsigned default 4000000; 2. DECLARE l_numeric number(8,2) DEFAULT 9.95; 3. DECLARE l_date date DEFAULT '1999-12-31'; 4. DECLARE l_datetime datetime DEFAULT '1999-12-31 23:5 9:59'; 5.DECLARE l_varchar varchar(255) DEFAULT 'This will not be padded'; ?. 变量赋值 SET 变量名 = 表达式值 [,variable_name = expression ...] select col_name[,....] into var_name[,....] from table 条件 实例:动态查询f_name,f_price 给ruitname,和fruitprice赋值 select f_name,f_price into fruitname,fruitprice from fruits where f_id=’a1’; (5). 注释 MySQL存储过程可使用两种风格的注释 双模杠:-- 该风格一般用于单行注释 c风格:/* 注释内容 */ 一般用于多行注释 例如: 流程控制使用 1:if 语句 例10.14】IF语句的示例,代码如下: if() { 功能代码; }else { 功能代码; } if then else end if; IF val IS NULL THEN SELECT 'val is NULL'; ELSE SELECT 'val is not NULL'; END IF; ?. if-then -else语句 1. mysql > DELIMITER // 2. mysql > CREATE PROCEDURE proc2(IN parameter int) 3. -> begin 4. -> declare var int; 5. -> set var=parameter+1; 6. -> if var=0 then 7. -> insert into t values(17); 8. -> end if; 9. -> if parameter=0 then 10. -> update t set s1=s1+1; 11. -> else 12. -> update t set s1=s1+2; 13. -> end if; 14. -> end; 15. -> // 16. mysql > DELIMITER ; 【例10.15】使用CASE流程控制语句判断val值等于1、等于2,或者两者都不等,语句如下: delimiter // create procedure procase() begin declare val int; set val =1; case val when 1 then select 'val is 1'; when 2 then select 'val is 2'; end case; end // case 变量 when 条件1 then 功能1; when 条件2 then 功能2; end case; 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; 1. mysql > DELIMITER // 2. mysql > CREATE PROCEDURE proc3 (in parameter int) 3. -> begin 4. -> declare var int; 5. -> set var=parameter+1; 6. -> case var 7. -> when 0 then 8. -> insert into t values(17); 9. -> when 1 then 10. -> insert into t values(18); 11. -> else 12. -> insert into t values(19); 13. -> end case; 14. -> end; 15. -> // 16. mysql > DELIMITER ; 【例10.16】使用CASE流程控制语句判断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; loop循环 【例10.17】使用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; leave 语句用来退出任何被标注的流程控制构造,leave 语句的基本格式如下: leave label iterate 语句将执行顺序转到语句开头处 语法格式 iterate label iterate 只可以出现在loop,repeat while 语句中 iterate 的意思是再次循环环 label参数表示循环的标志。iterate语句必须跟在循环标志前面。 1. mysql > DELIMITER // 2. mysql > CREATE PROCEDURE proc6 () 3. -> begin 4. -> declare v int; 5. -> set v=0; 6. -> LOOP_LABLE:loop 7. -> insert into t values(v); 8. -> set v=v+1; 9. -> if v >=5 then 10. -> leave LOOP_LABLE; 11. -> end if; 12. -> end loop; 13. -> end; 14. -> // 15. mysql > DELIMITER ; 【例10.18】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 mysql > DELIMITER // 1. 2. mysql > CREATE PROCEDURE proc10 () 3. -> begin 4. -> declare v int; 5. -> set v=0; 6. -> LOOP_LABLE:loop 7. -> if v=3 then 8. -> set v=v+1; 9. -> ITERATE LOOP_LABLE; 10. -> end if; 11. -> insert into t values(v); 12. -> set v=v+1; 13. -> if v>=5 then 14. -> leave LOOP_LABLE; 15. -> end if; 16. -> end loop; 17. -> end; 18. -> // 19. mysql > DELIMITER ; repeat 语句 创建一个带条件判断的循环过程,每次语句执行完毕之后,会对条件表达式进行判断,如果表达式为真,则循环环结束,否则重复执行循环中语句。 repeat 语法的基本格式如下: [repeat_label:]repeat statement_list util expr_condition end repeat[repeat:label] 【例10.19】REPEAT语句示例,id值小于等于10之前,将重复执行循环过程,代码如下: repeat 。。。。。。功能代码,循环体 until 条件 end repeat; DECLARE id INT DEFAULT 0; REPEAT SET id = id + 1; UNTIL id >= 10 END REPEAT; mysql > DELIMITER // 1. 2. mysql > CREATE PROCEDURE proc5 () 3. -> begin 4. -> declare v int; 5. -> set v=0; 6. -> repeat 7. -> insert into t values(v); 8. -> set v=v+1; 9. -> until v>=5 10. -> end repeat; 11. -> end; 12. -> // mysql > DELIMITER ; 【例10.20】WHILE语句示例,id值小于等于10之前,将重复执行循环过程,代码如下: while 条件 do 循环体; end while; delimiter // create procedure procwhile() DECLARE i INT DEFAULT 0; WHILE i < 10 DO SET i = i + 1; select now(); END WHILE; end // ?. while ???? end while: 1. mysql > DELIMITER // 2. mysql > CREATE PROCEDURE proc4() 3. -> begin 4. -> declare var int; 5. -> set var=0; 6. -> while var<6 do 7. -> insert into t values(var); 8. -> set var=var+1; 9. -> end while; 10. -> end; -> // 11. mysql > DELIMITER 光标的使用 查询语句可能返回多条记录,如果数据量非常大,需要在存储过程和存储函数中使用光标来逐条读取查询结果集中的记录。应用程序可以根据需要滚动或浏览其中的数据,本节将介绍如何声明,打开,使用关闭光标。 声明光标: declare cursor_name CURSOR FOR select_statement 例10.10】声明名称为cursor_fruit的光标,代码如下: DECLARE cursor_fruit CURSOR FOR SELECT f_name, f_price FROM fruits ; 打开光标: open cursor_name; 【例10.11】打开名称为cursor_fruit的光标,代码如下: OPEN cursor_fruit ; 使用光标: FETCH 光标名称 into var_name,var_price; 语法格式fetch cursor_name into var_name,var_price 【例10.12】使用名称为cursor_fruit的光标。将查询出来的数据存入fruit_name和fruit_price这两个变量中,代码如下: FETCH cursor_fruit INTO fruit_name, fruit_price ; 关闭光标 【例10.13】关闭名称为cursor_fruit的光标,代码如下: CLOSE cursor_fruit; 定义条件和处理程序 特定条件需要特定处理。这些条件可以联系到错误,以及子程序中一般流程控制。定义条件事先定义程序执行过程中遇到的问题,处理程序定义了在遇到这些问题是应当采取的处理方式,别且保证存储过程或函数在遇到警告或者错误时能继续执行。这样可以增强存储程序处理问题的能力,避免程序异常停止工作。 定义条件 declare condition_name condition for[condition_type] [condition_type]: sqlstate[value] sqlstate_value|mysql_error_code sqlstate_value和mysql_error_code都可以表示MySQL的 错误, sqlstate_value为长度为5的字符串类型的错误代码,mysql_error_code 为类型错误代码:例如error 1142(42000) 中,sqlstate_value的值是: 42000,my_error_code 的值是1142 【例10.7】定义"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 【例10.8】定义处理程序的几种方式,代码如下: //方法一:捕获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'; 【例10.9】定义条件和处理程序,具体执行的过程如下: 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 ; CALL handlerdemo(); /*调用存储过程*/ SELECT @x; /*查看调用过程结果*/ 存储函数:创建存储函数 使用create function(存储函数参数列表) returns 返回的数据类型 return(select s_name from suppliers where s_call=’48075’); 调用此存储函数 select 存储函数名称(); delimiter // create function count() returns int; return (select count(* ) from fruits where s_id=103); MySQL存储过程的调用 用call和你过程名以及一个括号,括号里面根据需要,加入参数,参数包括输入参数、输出参数、输入输出参数。具体的调用方法可以参看上面的例子。 call proccedure_name([param]); 5. MySQL存储过程的查询 我们像知道一个数据库下面有那些表,我们一般采用show tables; 进行查看。那么我们要查看某个数据库下面的存储过程,是否也可以采用呢,答案是,我们可以查看某个数据库下面的存储过程,但是另一种方式。 我们可以用 show procedure status; 模糊查询某个数据库的存储过程 show procedure status like ‘p%’; 进行查询。 如果我们想知道,某个存储过程的详细,那我们又该怎么做呢,是不是也可以像操作表一样用describe 表名进行查看呢, 答案是:我们可以查看存储过程的详细,但是需要用另一种方法: SHOW CREATE PROCEDURE 数据库.存储过程名; 例如:查询数据库test1中proIF存储过程的详细信息 show create procedure test1.proIf g; 就可以查看当前存储过程的详细。 6. MySQL存储过程的修改 ALTER PROCEDURE 更改用CREATE PROCEDURE 建立的预先指定的存储过程,其不会影响相关存储过程或存储功能。 7. MySQL存储过程的删除 删除一个存储过程比较简单,和删除表一样: DROP PROCEDURE 例如删除:test中的proIF drop procedure proIF; 从MySQL的表格中删除一个或多个存储过程。 存储过程的实例: 创建一个名称为sch的数据表,表结构如表10.1所示将表10.2中的数据插入到sch表中 id int(10) 主键 非空 唯一, name varchar(50) 非空, glass varchar(50) 非空 sch表内容 id name glass 1 xiaoming glass1 2 xiaojun glass2 步骤1:创建一个sch表,并且向sch表中插入表格中的数据 CREATE TABLE sch(id INT, name VARCHAR(50),glass VARCHAR(50)); INSERT INTO sch VALUE(1,’xiaoming’,’1班’), (1,’xiaojun’,’2班’); 步骤2:创建一个存储函数用来统计表sch中记录数 /*创建存储函数*/ DELIMITER // CREATE FUNCTION count_sch() RETURNS INT RETURN (SELECT COUNT(*) FROM sch); // /*调用存储函数*/ SELECT count_sch() // DELIMITER ; 步骤3:创建一个存储过程,通过调用存储函数的方法来获取表sch中记录数和sch表中id的和。 /*创建存储过程*/ DELIMITER // CREATE PROCEDURE add_id(out count INT) BEGIN DECLARE itmp INT; DECLARE cur_id CURSOR FOR SELECT id FROM sch; DECLARE EXIT HANDLER FOR NOT FOUND CLOSE cur_id; SELECT count_sch() INTO count; SET @sum=0; OPEN cur_id; REPEAT FETCH cur_id INTO itmp; IF itmp<10 THEN SET @sum= @sum+itmp; END IF; UNTIL 0 END REPEAT; CLOSE cur_id; END // /*查看存储过程调用后的结果*/ SELECT @a, @sum // DELIMITER ;
|