---------------什么是存储过程---------------
  存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。
一个存储过程是一个可编程的函数,它在数据库中创建并保存。它可以有SQL语句和一些特殊的控制结构组成。当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的。数据库中的存储过程可以看做是对编程中面向对象方法的模拟。它允许控制数据的访问方式。


-------------存储过程有什么优缺点-----------
优点:
  1. 速度快:我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程是预编译的。在首次运行一个存储过程时查询,优化器对其进行分析优化,并且给出最终被存储在系统表中的执行计划
  2. 封装、简单调用:存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句。而且数据库专业人员可以随时对存储过程进行修改,对应用程序源代码毫无影响。
  3. 可以添加逻辑处理:存储过程可以用流控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。
  4. 限制与安全:管理员通过执行某一存储过程的权限进行限制,能够实现对相应的数据的访问权限的限制,避免了非授权用户对数据的访问,保证了数据的安全。
  5. 减少网络流量:针对同一个数据库对象的操作(如查询、修改),如果这一操作所涉及的Transaction-SQL语句被组织程存储过程,那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,从而大大增加了网络流量并降低了网络负载。
  6. 增加代码执行效率:一个复杂的数据操作。如果你在前台处理的话。可能会涉及到多次数据库连接。但如果你用存储过程的话。就只有一次。从响应时间上来说有优势。
  7. 稳定:程序容易出现 BUG 不稳定,而存储过程,只要数据库不出现问题,基本上是不会出现什么问题的。也就是说从安全上讲,使用了存储过程的系统更加稳定。
缺点:
  1.存储过程的开发调试要比一般程序困难:业务逻辑复杂,想要在代码中调试很麻烦。
  2.移植性:一般存储过程总是绑定某个数据库的,不然就无法靠优化数据库访问来提高性能了。
----------------存储过程入门------------------

一、我的第一个存储过程

DROP PROCEDURE IF EXISTS `myfirst`;
delimiter $
create procedure myfirst(in number int)
begin
 select number;
end
$
delimiter ;
#执行存储过程,这里执行完就是显示500
CALL myfirst(500);





二、存储过程基础知识讲解:


1.声明分割符:


这里需要注意的是delimiter $delimiter ;两句,delimiter是分割符的意思,因为MySQL默认以";"为分隔符,在存储过程中";"往往不代表指令结束,马上运行,而delimiter原本就是";"的意思,因此用这个命令转换一下";"为"$",这样只有收到"$"才认为指令结束.这样MySQL才会将";"当做存储过程中的代码,不会执行这些代码,用完了之后要把分隔符还原。


2.参数:


MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT,形式如:


CREATE PROCEDURE([[IN |OUT |INOUT ] 参数名 数据类形...])


IN 输入参数:表示该参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值


OUT 输出参数:该值可在存储过程内部被改变,并可返回


INOUT 输入输出参数:调用时指定,并且可被改变和返回


(1).in参数例子


MySQL 存储过程 “in” 参数:跟 C 语言的函数参数的值传递类似, MySQL 存储过程内部可能会修改此参数,但对 in 类型参数的修改,对调用者(caller)来说是不可见的(not visible)。


#创建存储过程
drop procedure if exists myfirst_in;
delimiter $
create procedure myfirst_in(in number int)
begin
if(number is not null) then
set number=12;
end if;
select number as inner_number;
end
$
DELIMITER ;


#执行存储过程
SET @number=5;
CALL myfirst_in(@number);
SELECT @number AS out_number;
可以看到:用户变量 @number 传入值为5,执行存储过程后,在过程内部值为:12(inner_number),但外部变量值依旧为:5(out_number)。



(2).out参数例子


MySQL 存储过程 “out” 参数:从存储过程内部传值给调用者。在存储过程内部,该参数初始值为 null,无论调用者是否给存储过程参数设置值。


#创建存储过程
DROP PROCEDURE IF EXISTS myfirst_out;
DELIMITER $
CREATE PROCEDURE myfirst_out(OUT number INT)
BEGIN
#select number as inner_1;
IF (number IS NOT NULL) THEN
  SET number=number+1;
  #SELECT number AS inner_2;
#ELSE
  #SET number=15;
  #SELECT number AS inner_3;
END IF;
  SELECT 5 INTO number;
END
$
DELIMITER ;


#执行存储过程
SET @number=3;
CALL myfirst_out(@number);
SELECT @number AS out_number;
可以看到:因为实在sqlyog中,不会显示所有的结果所以上面的 inner_1,inner_2,inner_3我都是注释掉一个一个测试,可以看到inner_1,inner_2,inner_3都是null值,中间无论怎么修改那个number都没用,只有在执行完成才将number=5返回.



(3).inout参数例子


MySQL 存储过程 inout 参数跟 out 类似,都可以从存储过程内部传值给调用者。不同的是:调用者还可以通过 inout 参数传递值给存储过程。


#创建存储过程
drop procedure if exists myfirst_inout;
delimiter $
create procedure myfirst_inout(inout number int)
begin
if(number is not null)then
set number=number+5;
end if;
end
$
delimiter ;


#执行存储过程
set @number=15;
call myfirst_inout(@number);
select @number as out_number;
可以看到:返回值结果为20,我们把 @number(15),传给存储过程后,存储过程最后又把计算结果值 20(out_number)传回给调用者。



通过以上例子:如果仅仅想把数据传给 MySQL 存储过程,那就使用"in" 类型参数;如果仅仅从 MySQL 存储过程返回值,那就使用"out" 类型参数;如果需要把数据传给 MySQL 存储过程,还要经过一些计算后再传回给我们,此时,要使用"inout" 类型参数。




3.变量


(1).变量定义


格式:DECLARE variable_name datatype DEFAULT value;


DECLARE id INT; 或者 DECLARE id INT DEFAULT 10;


(2).变量赋值


格式:set 变量名=值


(3).用户变量


在mysql客户端 set @number=15;


调用上面两个存储过程操作的是同一个变量,用户变量可以在不同的存储过程中使用。


4.存储过程的调用


call 存储过程名称。如:call myfirst();


5.查询存储过程


我们可以用


select name from mysql.proc where db='数据库名';


或者


select routine_name from information_schema.routines where routine_schema='数据库名';


或者


show procedure status where db='数据库名';


进行查询。


如果我们想知道,某个存储过程的详细,那我们又该怎么做呢?是不是也可以像操作表一样用describe 表名进行查看呢?


答案是:我们可以查看存储过程的详细,但是需要用另一种方法:


SHOW CREATE PROCEDURE 数据库.存储过程名;


就可以查看当前存储过程的详细。


6.存储过程删除


和删除表类似 drop procedure 名称 如:drop procedure myfirst;


7.存储过程的控制语句


(1).条件语句


格式:if-then-elseif-then-else-end if;


例如:


DROP PROCEDURE IF EXISTS myfirst_if;
DELIMITER $
CREATE PROCEDURE myfirst_if(IN number INT)
BEGIN
IF number=0 THEN
SELECT number;
ELSEIF (number=1)THEN
SELECT 1;
ELSE 
SELECT 3;
END IF;
END
$
DELIMITER ;



(2).case语句


格式:case 变量 when 值 then else end case;


例如:


drop procedure if exists myfirst_case;
delimiter $
create procedure myfirst_case(in number int)
begin
case number
when 0 then
select 0;
when 1 then
select 1;
when 2 then
select 2;
else
select 3;
end case;
end
$
delimiter ;



(3).循环语句


格式:while 表达式 do···· end while


例如:


drop procedure if exists myfirst_while;
delimiter $
create procedure myfirst_while()
begin
declare var int default 0;
while var<5 do
set var=var+1;
end while;
select var;
end
$
delimiter ;



格式:repeat···· until 表达式 end repeat


它在执行操作后检查结果,而while则是执行前进行检查。


例如:


DROP PROCEDURE IF EXISTS myfirst_repeat;
DELIMITER $
CREATE PROCEDURE myfirst_repeat()
BEGIN
DECLARE number INT DEFAULT 0;
REPEAT
SET number=number+1;
UNTIL number>55#until 不执行的条件
END REPEAT;
SELECT number;
END
$
DELIMITER ;



格式:循环标识:LOOP ...leave 循环标识 end loop;


loop循环不需要初始条件,这点和while 循环相似,同时和repeat循环一样不需要结束条件, leave语句的意义是离开循环。


例如:


DROP PROCEDURE IF EXISTS myfirst_loop;
DELIMITER $
CREATE PROCEDURE myfirst_loop()
BEGIN
DECLARE number INT DEFAULT 0;
LOOP_LABLE:LOOP
SET number=number+1;
IF (number>5)THEN
LEAVE LOOP_LABLE;
END IF;
END LOOP;
SELECT number;
END
$
DELIMITER ;



(4).ITERATE迭代


DROP PROCEDURE IF EXISTS myfirst_iterator;
DELIMITER $
CREATE PROCEDURE myfirst_iterator()
BEGIN
DECLARE number INT DEFAULT 0;
LOOP_LABLE:LOOP
SET number=number+1;
IF(number=3)THEN
ITERATE LOOP_LABLE;
END IF;
INSERT INTO t VALUES(number);
IF (number>5)THEN
LEAVE LOOP_LABLE;
END IF;
END LOOP;
SELECT number;
END
$
DELIMITER ;

当number=3时,重新回到LOOP_LABLE,不执行下面的insert语句了,类似于java里面的continue。




(5).存储过程游标使用


DROP PROCEDURE IF EXISTS myfirst_cursor;
DELIMITER $
CREATE PROCEDURE myfirst_cursor()
BEGIN

-- 定义接受游标数据的变量
DECLARE username VARCHAR(50);
-- 遍历数据结束标志
DECLARE down INT DEFAULT FALSE;
-- 游标
DECLARE cur CURSOR FOR SELECT `name` FROM `company`;
-- 将结束标志绑定到游标上
DECLARE CONTINUE HANDLER FOR NOT FOUND SET down=TRUE;
-- 打开游标
OPEN cur;

-- 开始循环
read_loap:LOOP
-- 提取游标里的数据,这里只有一个,多个的话也一样;
FETCH cur INTO username;
-- 声明结束
IF down THEN 
LEAVE read_loap;
END IF;

-- 这里做想做的循环事件
INSERT INTO `customer`(`name`) VALUES(username);

-- 结束循环
END LOOP;
-- 关闭游标
CLOSE cur;


END
$
DELIMITER ;



8.MySQL存储过程的基本函数


(1).字符串类


CHARSET(str) //返回字串字符集
CONCAT (string2 [,... ]) //连接字串
INSTR (string ,substring ) //返回substring首次在string中出现的位置,不存在返回0
LCASE (string2 ) //转换成小写
LEFT (string2 ,length ) //从string2中的左边起取length个字符
LENGTH (string ) //string长度
LOAD_FILE (file_name ) //从文件读取内容
LOCATE (substring , string [,start_position ] ) 同INSTR,但可指定开始位置
LPAD (string2 ,length ,pad ) //重复用pad加在string开头,直到字串长度为length
LTRIM (string2 ) //去除前端空格
REPEAT (string2 ,count ) //重复count次
REPLACE (str ,search_str ,replace_str ) //在str中用replace_str替换search_str
RPAD (string2 ,length ,pad) //在str后用pad补充,直到长度为length
RTRIM (string2 ) //去除后端空格
STRCMP (string1 ,string2 ) //逐字符比较两字串大小,
SUBSTRING (str , position [,length ]) //从str的position开始,取length个字符

注:mysql中处理字符串时,默认第一个字符下标为1,即参数position必须大于等于1






(2).数学类


ABS (number2 ) //绝对值
BIN (decimal_number ) //十进制转二进制
CEILING (number2 ) //向上取整
CONV(number2,from_base,to_base) //进制转换
FLOOR (number2 ) //向下取整
FORMAT (number,decimal_places ) //保留小数位数
HEX (DecimalNumber ) //转十六进制
注:HEX()中可传入字符串,则返回其ASC-11码,如HEX('DEF')返回4142143
也可以传入十进制整数,返回其十六进制编码,如HEX(25)返回19
LEAST (number , number2 [,..]) //求最小值
MOD (numerator ,denominator ) //求余
POWER (number ,power ) //求指数
RAND([seed]) //随机数
ROUND (number [,decimals ]) //四舍五入,decimals为小数位数]

注:返回类型并非均为整数,如:


(1)默认变为整形值


mysql> select round(1.23);   

 +-------------+   

 | round(1.23) |   

 +-------------+   

 |           1 |   

 +-------------+   

 1 row in set (0.00 sec)   

   

 mysql> select round(1.56);   

 +-------------+   

 | round(1.56) |   

 +-------------+   

 |           2 |   

 +-------------+   

 1 row in set (0.00 sec)



(2)可以设定小数位数,返回浮点型数据


mysql> select round(1.567,2);   

 +----------------+   

 | round(1.567,2) |   

 +----------------+   

 |           1.57 |   

 +----------------+   

 1 row in set (0.00 sec)  

 SIGN (number2 ) //






(3).日期时间类


ADDTIME (date2 ,time_interval ) //将time_interval加到date2
CONVERT_TZ (datetime2 ,fromTZ ,toTZ ) //转换时区
CURRENT_DATE ( ) //当前日期
CURRENT_TIME ( ) //当前时间
CURRENT_TIMESTAMP ( ) //当前时间戳
DATE (datetime ) //返回datetime的日期部分
DATE_ADD (date2 , INTERVAL d_value d_type ) //在date2中加上日期或时间
DATE_FORMAT (datetime ,FormatCodes ) //使用formatcodes格式显示datetime
DATE_SUB (date2 , INTERVAL d_value d_type ) //在date2上减去一个时间
DATEDIFF (date1 ,date2 ) //两个日期差
DAY (date ) //返回日期的天
DAYNAME (date ) //英文星期
DAYOFWEEK (date ) //星期(1-7) ,1为星期天
DAYOFYEAR (date ) //一年中的第几天
EXTRACT (interval_name FROM date ) //从date中提取日期的指定部分
MAKEDATE (year ,day ) //给出年及年中的第几天,生成日期串
MAKETIME (hour ,minute ,second ) //生成时间串
MONTHNAME (date ) //英文月份名
NOW ( ) //当前时间
SEC_TO_TIME (seconds ) //秒数转成时间
STR_TO_DATE (string ,format ) //字串转成时间,以format格式显示
TIMEDIFF (datetime1 ,datetime2 ) //两个时间差
TIME_TO_SEC (time ) //时间转秒数]
WEEK (date_time [,start_of_week ]) //第几周
YEAR (datetime ) //年份
DAYOFMONTH(datetime) //月的第几天
HOUR(datetime) //小时
LAST_DAY(date) //date的月的最后日期
MICROSECOND(datetime) //微秒
MONTH(datetime) //月
MINUTE(datetime) //分返回符号,正负或0
SQRT(number2) //开平方