MySql存储过程的作用及语法

MySQL使用存储过程的作用

1、使用了存过程,很多相似性的删除,更新,新增等操作就变得轻松了,并且以后也便于管理!

2、存储过程因为SQL语句已经预编绎过了,因此运行的速度比较快。   

3、存储过程可以接受参数、输出参数、返回单个或多个结果集以及返回值。可以向程序返回错误原因。     

4、存储过程运行比较稳定,不会有太多的错误。只要一次成功,以后都会按这个程序运行。     

5、存储过程主要是在服务器上运行,减少对客户机的压力。   

6、存储过程可以包含程序流、逻辑以及对数据库的查询。同时可以实体封装和隐藏了数据逻辑。   

7、存储过程可以在单个存储过程中执行一系列SQL语句。   

8、存储过程可以从自己的存储过程内引用其它存储过程,这可以简化一系列复杂语句。

MySQL使用存储过程的争议

尽管存储过程有诸多优点,但是对于存储过程的使用,一直都存在着很多争议,比如有些公司对于大型项目要求使用存储过程,而有些公司在手册中明确禁止使用存储过程,为什么这些公司对存储过程的使用需求差别这么大呢?

我们得从存储过程的特点来找答案。

你能看到存储过程有很多好处。

首先存储过程可以一次编译多次使用。存储过程只在创造时进行编译,之后的使用都不需要重新编译,这就提升了 SQL 的执行效率。其次它可以减少开发工作量。将代码封装成模块,实际上是编程的核心思想之一,这样可以把复杂的问题拆解成不同的模块,然后模块之间可以重复使用,在减少开发工作量的同时,还能保证代码的结构清晰。还有一点,存储过程的安全性强,我们在设定存储过程的时候可以设置对用户的使用权限,这样就和视图一样具有较强的安全性。最后它可以减少网络传输量,因为代码封装到存储过程中,每次使用只需要调用存储过程即可,这样就减少了网络传输量。同时在进行相对复杂的数据库操作时,原本需要使用一条一条的 SQL 语句,可能要连接多次数据库才能完成的操作,现在变成了一次存储过程,只需要连接一次即可。

基于上面这些优点,不少大公司都要求大型项目使用存储过程,比如微软、IBM 等公司。但是国内的阿里并不推荐开发人员使用存储过程,这是为什么呢?

存储过程虽然有诸如上面的好处,但缺点也是很明显的。

它的可移植性差,存储过程不能跨数据库移植,比如在 MySQL、Oracle 和 SQL Server 里编写的存储过程,在换成其他数据库时都需要重新编写。

其次调试困难,只有少数 DBMS 支持存储过程的调试。对于复杂的存储过程来说,开发和维护都不容易。

此外,存储过程的版本管理也很困难,比如数据表索引发生变化了,可能会导致存储过程失效。我们在开发软件的时候往往需要进行版本管理,但是存储过程本身没有版本控制,版本迭代更新的时候很麻烦。

最后它不适合高并发的场景,高并发的场景需要减少数据库的压力,有时数据库会采用分库分表的方式,而且对可扩展性要求很高,在这种情况下,存储过程会变得难以维护,增加数据库的压力,显然就不适用了。

了解了存储过程的优缺点之后,我想说的是,存储过程既方便,又有局限性。尽管不同的公司对存储过程的态度不一,但是对于我们开发人员来说,不论怎样,掌握存储过程都是必备的技能之一。

MySQL存储过程的创建 

先定义一个结束分隔符,控制sql语句的执行

delimiter $      //意思是用$作为sql语句的结束符

参数 

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

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

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

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

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

①IN参数例子 

创建: 

DELIMITER $ 
CREATE PROCEDURE demo(IN p_in int) 
 BEGIN 
 SELECT p_in; 
 SET p_in=2; 
 SELECT p_in; 
END$

执行结果: 

SET @p_in=1;
CALL demo(@p_in);
+------+ 
| p_in | 
+------+ 
| 1    | 
+------+ 
+------+ 
| p_in | 
+------+ 
| 2    | 
+------+
SELECT @p_in;
+-------+ 
| @p_in | 
+-------+ 
|  1    | 
+-------+

以上可以看出,p_in虽然在存储过程中被修改,但并不影响@p_id的值 

②OUT参数例子 

创建: 

DELIMITE $
CREATE PROCEDURE demo(OUT p_out int) 
 BEGIN 
 SELECT p_out; 
 SET p_out=2; 
 SELECT p_out; 
 END$

执行结果: 

SET @p_out=1; 
CALL demo(@p_out);
+-------+ 
| p_out | 
+-------+ 
| NULL  | 
+-------+ 
+-------+ 
| p_out | 
+-------+ 
| 2     | 
+-------+
SELECT @p_out; 
+-------+ 
| p_out | 
+-------+ 
| 2     | 
+-------+

 

③INOUT参数例子 

创建: 

DELIMITER $
CREATE PROCEDURE demo_inout_parameter(INOUT p_inout int) 
 BEGIN 
 SELECT p_inout; 
 SET p_inout=2; 
 SELECT p_inout; 
 END$

执行结果: 

SET @p_inout=1; 
CALL demo_inout_parameter(@p_inout) ;
+---------+ 
| p_inout | 
+---------+ 
| 1       | 
+---------+ 
+---------+ 
| p_inout | 
+---------+ 
| 2       | 
+---------+
SELECT @p_inout;
+----------+ 
| @p_inout | 
+----------+ 
| 2        | 
+----------+

 

变量

①定义变量

例如: 

DECLARE l_int int unsigned default 4000000; 
DECLARE l_numeric number(8,2) DEFAULT 9.95; 
DECLARE l_date date DEFAULT '1999-12-31'; 
DECLARE l_datetime datetime DEFAULT '1999-12-31 23:59:59'; 
DECLARE l_varchar varchar(255) DEFAULT 'This will not be padded';

②变量赋值 

SET 变量名 = 表达式值 [,variable_name = expression ...] 

③用户变量 

ⅰ. 在MySQL客户端使用用户变量 

SELECT 'Hello World' into @x; 
SELECT @x; 
+-------------+ 
| @x          | 
+-------------+ 
| Hello World | 
+-------------+ 
SET @y='Goodbye Cruel World'; 
SELECT @y; 
+---------------------+ 
| @y               | 
+---------------------+ 
| Goodbye Cruel World | 
+---------------------+ 
SET @z=1+2+3; 
SELECT @z; 
+------+ 
| @z   | 
+------+ 
| 6    | 
+------+

ⅱ. 在存储过程中使用用户变量 

CREATE PROCEDURE GreetWorld( ) 
 BEGIN
 SELECT CONCAT(@greeting,' World'); 
 SET @greeting='Hello'; 
 END$
 CALL GreetWorld( ); 
+----------------------------+ 
| CONCAT(@greeting,' World') | 
+----------------------------+ 
| Hello World              | 
+----------------------------+

 

④MySQL存储过程的修改 

ALTER PROCEDURE

更改用CREATE PROCEDURE 建立的预先指定的存储过程,其不会影响相关存储过程或存储功能。 

⑤MySQL存储过程的删除 

删除一个存储过程比较简单,和删除表一样: 

DROP PROCEDURE

从MySQL的表格中删除一个或多个存储过程。

⑥MySQL存储过程的控制语句 

(1). 变量作用域 

内部的变量在其作用域范围内享有更高的优先权,当执行到end。变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储 

过程外再也不能找到这个申明的变量,但是你可以通过out参数或者将其值指派 

给会话变量来保存其值。 

CREATE PROCEDURE proc3() 
 begin 
 declare x1 varchar(5) default 'outer'; 
 begin 
 declare x1 varchar(5) default 'inner'; 
 select x1; 
 end; 
 select x1; 
 end$

(2). 条件语句 

A:if-then -else语句 

CREATE PROCEDURE proc2(IN parameter int) 
 begin 
 declare var int; 
 set var=parameter+1; 
 if var=0 then 
 insert into t values(17); 
 end if; 
 if parameter=0 then 
 update t set s1=s1+1; 
 else 
 update t set s1=s1+2; 
 end if; 
 end$

B:case语句: 

CREATE PROCEDURE proc3 (in parameter int) 
 begin 
 declare var int; 
 set var=parameter+1; 
 case var 
 when 0 then 
 insert into t values(17); 
 when 1 then 
 insert into t values(18); 
 else 
 insert into t values(19); 
 end case; 
 end$

C:loop ·····end loop: 

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

CREATE PROCEDURE proc6 () 
 begin 
 declare v int; 
 set v=0; 
 LOOP_LABLE:loop 
 insert into t values(v); 
 set v=v+1; 
 if v >=5 then 
 leave LOOP_LABLE; 
 end if; 
 end loop; 
 end$

D:while语句    while ···· end while:

CREATE PROCEDURE proc4()  
 begin 
 declare var int;  
 set var=0;  
 while var<6 do  
 insert into t values(var);  
 set var=var+1;  
 end while;  
 end$

E:repeat 语句       repeat···· end repeat:

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

CREATE PROCEDURE proc5 ()  
 begin   
 declare v int;  
 set v=0;  
 repeat  
 insert into t values(v);  
 set v=v+1;  
 until v>=5  
 end repeat;  
 end$

⑦ITERATE迭代 

 ITERATE: 

通过引用复合语句的标号,来从新开始复合语句 

CREATE PROCEDURE proc10 () 
 begin 
 declare v int; 
 set v=0; 
 LOOP_LABLE:loop 
 if v=3 then 
 set v=v+1; 
 ITERATE LOOP_LABLE; 
 end if; 
 insert into t values(v); 
 set v=v+1; 
 if v>=5 then 
 leave LOOP_LABLE; 
 end if; 
 end loop; 
 end$

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 

select substring('abcd',0,2); 
+-----------------------+ 
| substring('abcd',0,2) | 
+-----------------------+ 
+-----------------------+
select substring('abcd',1,2); 
+-----------------------+ 
| substring('abcd',1,2) | 
+-----------------------+ 
| ab                | 
+-----------------------+
TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符 
UCASE (string2 ) //转换成大写 
RIGHT(string2,length) //取string2最后length个字符 
SPACE(count) //生成count个空格

(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)默认变为整形值 

select round(1.23); 
+-------------+ 
| round(1.23) | 
+-------------+ 
| 1           | 
+-------------+
select round(1.56); 
+-------------+ 
| round(1.56) | 
+-------------+ 
| 2           | 
+-------------+

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

select round(1.567,2); 
+----------------+ 
| round(1.567,2) | 
+----------------+ 
| 1.57           | 
+----------------+

 

(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) //小时

视频学习中的例子

1

create procedure p1()
begin
    select 'hello' from dual;
end$
call p1()$

create procedure p2()
begin
declare age int default 18;
declare height int default 180;
select concat('年龄是',age,'身高是',height);
end$

3

create procedure p3()
begin
declare age int default 18;
set age:= age+20;
select concat('20年后年龄是',age);
end$

4

create procedure p4()
begin
declare age int default 18;
if age >=18 then
select  '成年';
else
select '未成年';
end if;
end$

5

create procedure p5(width int ,height int)
begin
select concat('你的面积是', width * height ) as area;
if width >height then
select '你挺胖';;
elseif width < height then
select ‘你挺瘦’;
else
select '你挺方';
endif;
end$

6

create procedure p6()
begin
declare total int default 0;
declare num int default 0;
while num <100 do
set num:=num+1;
set total:= total +num;
end while;
select total;
end$

7

create procedure p7(in n int)
begin
declare total int default 0;
declare num int default 0;
while num <n do
set num:=num+1;
set total:= total +num;
end while;
select total;
end$

8

create procedure p8(in n int ,out total int)
begin
declare num int default 0;
set total :=0; #null  碰到任何操作都会变成null
while num<n do
set num:=num+1;
set total :=total+num;
end while;
end$
call p8(100,@summary)$
select @summary

9

create procedure p9(inout age int)
begin
set age:=age +20;
end$
set @currage =18$
call p9(@currage)$
select @currage$

10

create procedure p10()
begin
declare pos int default 0;
set pos := floor(5*rand());
case pos
when 1 then select 'still flying';
when 2 then select 'fall in sea';
when 3 then select 'in the island';
else select 'I don't know';
end case;
end$

11

create procedure p11()
begin
declare total int default 0;
declare i int default 0;
repeat 
set i:=i+1;
set total :=total +i;
until i>=100 end repeat;
select total;
end$

12  游标 

create procedure p12()
begin
declare row_gid int;
declare row_num int;
declare row_name varchar(20);
declare getgoods cursor for select gid,num,name from goods;
open getgoods;
fetch getgoods into row_gid,row_num,row_name;
select row_num,row_name;
close getgoods;
end$

13

create procedure p13()
begin
declare row_gid int;
declare row_num int;
declare row_name varchar(20);
declare getgoods cursor for select gid, num , name from goods;
open getgoods;
fetch getgoods into row_gid,row_num,row_name; #cat
select row_num,row_name;
fetch getgoods into row_gid,row_num,row_name;#dog
select row_num,row_name;
fetch getgoods into row_gid,row_num,row_name;#pig
select row_num,row_name;
fetch getgoods into row_gid,row_num,row_name;#error
select row_num,row_name;
close getgoods;
end$

#逻辑处理游标越界

14

create procedure p14()
begin
declare row_gid int;
declare row_num int;
declare row_name varchar(20);
declare cnt int default 0;
declare i int default 0;
declare getgoods cursor for select gid, num , name from goods;
select count(*) into cnt from goods;
open getgoods;
repeat set 
fetch getgoods into row_gid,row_num,row_name; #cat
select row_num,row_name;
until i >= cnt  end repeat;
close getgoods;
end$

15

游标越界的时候,在mysql  cursor中有一个标示,可以declare continue  handler 来操作一个越界标示

declare continue handler for NOT FOUND statement;
create procedure p15()
begin
declare row_gid int;
declare row_num int;
declare row_name varchar(20);
declare you int default 1;
declare getgoods cursor for select gid, num , name from goods;
select count(*) into cnt from goods;
declare continue handler for NOT FOUND set you :=0;  //如果发生not found事件,把you  改为 0
open getgoods;
repeat set 
fetch getgoods into row_gid,row_num,row_name;
select row_num,row_name;
until you=0 end repeat;
close getgoods;
end$
call p15()$
show warnings$

16:上条BUG修复,continue和 exit的区别(exit触发后后面的代码不在执行,而continue继续执行)

create procedure p16()
begin
declare row_gid int;
declare row_num int;
declare row_name varchar(20);
declare you int default 1;
declare getgoods cursor for select gid, num , name from goods;
select count(*) into cnt from goods;
declare exit handler for NOT FOUND set you :=0;#这里的handler类型改成了exit
open getgoods;
repeat set 
fetch getgoods into row_gid,row_num,row_name; 
select row_num,row_name;
until you=0 end repeat;
close getgoods;
end$

17:正确严谨的逻辑

#接下来的存储过程才应该是游标的正确使用方式:

#一定要用continue handler  ,通过逻辑来控制。

create procedure p17()
begin
declare row_gid int;
declare row_num int;
declare row_name varchar(20);
declare you int default 1;
declare getgoods cursor for select gid, num , name from goods;
select count(*) into cnt from goods;
declare continue handler for NOT FOUND set you :=0;
open getgoods;
fetch getgoods into row_gid,row_num,row_name; 
repeat set 
select row_num,row_name;
fetch getgoods into row_gid,row_num,row_name; 
until you=0 end repeat;
close getgoods;
end$

#换成while 循环

18

create procedure p18()
begin
declare row_gid int;
declare row_num int;
declare row_name varchar(20);
declare you int default 1;
declare getgoods cursor for select gid, num , name from goods;
select count(*) into cnt from goods;
declare continue handler for NOT FOUND set you :=0;
open getgoods;
fetch getgoods into row_gid,row_num,row_name; 
while you =1 do
select row_num,row_name;
fetch getgoods into row_gid,row_num,row_name; 
end while;
close getgoods;
end$
posted @ 2017-05-17 16:24  温柔的风  阅读(771)  评论(0编辑  收藏  举报