MySQL(六)——存储过程和存储函数

前言    

    今天简单的介绍一下“存储函数”和“存储过程”,平时在工作中用到的时间不多,时间长了难免会忘记。在这里简单的做个回忆总结,方便自己以后复习回忆,当然能帮到需要的同学那就更好了。

    关于MySQL中的存储过程/函数可以类比我们java中的方法进行理解,它们都是对一些经常要用到的代码打包封装到一个方法中,在需要用到的地方直接调用方法名就可以了,这样就省去了很多重复代码的编写。当然对于MySQL中存储过程/方法的调用同java中的方法有些许不同,我们会在下面详细说明。另外有一点就是我们在执行SQL语句的时候涉及到两个步骤:1.和mysql服务器进行连接;2.对原始SQL语句进行编译。经过这两个步骤后我们才能真正的从数据库中查到数据(或其他CRUD操作)。现在设想一下,假如我现在编写了5条SQL语句,要分别去执行,这时候就需要分别和MySQL服务器连接5次,并进行5次SQL语句的编译操作,很明显这对于性能是有影响的。但如果我们把这5条SQL打包到一个存储过程/函数中进行执行,就只需要和mysql服务器进行一次连接,而且在第一次调用完这个存储过程/函数后,再进行第二次、第三次...的调用时,可以直接执行第一次调用时编译的SQL语句。所以在运用了存储过程/函数后,原本的5条SQL语句现在只需要进行1次连接和1次编译就能完成原5次才能完成的功能,显而易见性能得到了提升。

    接下来进入正题,文章主要分四个部分,分别介绍一下:存储过程、存储函数、存储过程和存储函数的区别、实战案例。

目录

一、存储过程

二、存储函数

三、存储过程和存储函数的区别

四、案例实战


一、存储过程

1.1存储过程的定义格式

delimiter $

create procedure procedure_name(参数列表)

begin

    存储过程体(一组合法的SQL语句)

end $

delimiter ;

以上就是存储过程的定义格式,我们分开做一下解释:

  • create procedure:创建存储过程的关键字,固定写法
  • procedure_name:存储过程的名称,这个可以根据自己的喜好自定义
  • 参数列表:这个类似于java中方法的参数,里面可以为空、in、out、inout四种类型

            in:表示该参数可以作为输入,需要调用方在调用这个存储过程的时候传入具体的值;类似于java中的方法参数。

            out:表示该参数可以作为返回值;因为存储过程不像存储函数可以通过returns返回具体的值,所以这个参数就可以作为容器接收存储过程执行后的具体返回值。

            inout:该参数既可以作为输入也可以作为输出;它是上面两个的结合体,即既可以作为输入参数也可以作为容器接收返回值。

  • begin、end:存储过程体都包裹在begin和end中间。但假如我们的存储过程体只有一句SQL语句,那么begin和end这两个关键字可以省略。
  • delimiter $、delimiter ;  :这两个语句我们需要说明一下:因为在存储过程体中会有许多SQL语句,而每条SQL语句都是以分号结尾的,为了防止mysql在执行存储过程的时候遇到分号就结束,我们需要提前做好声明,告诉mysql我的这个存储过程是以$符表示结束的,这也就是为什么我们在end后面也加了一个$符号。存储过程执行结束后,我们需要把刚才设置的结束符$恢复为默认的分号";",所以在最后面我们又加了delimiter ;需要注意的一点是这个结束符你可以根据自己的喜好自定义,不一定非要是$,也可以是其他的比如#、&、*等等。

1.2存储过程的调用格式

定义好了存储过程以后我们就可以直接调用了,不同于java中直接写上方法名和实参就可以完成调用,MySQL中存储过程的调用需要在前面加上一个关键字:call,如下:

    call 存储过程名称(实际参数)

 1.3空参的存储过程

我们在“1.1存储过程的定义格式”中介绍过,存储过程的参数列表可以有空、in、out、inout四种类型,这里先介绍一下参数为空时存储过程的具体定义。假如我们现在需要往一张person表中插入5条数据,具体的存储过程怎么定义呢?如下:

//定义无参存储过程
delimiter $

create procedure mysql()

begin

    insert into person(name,age) values ('tom',13),('marry',14),
                                        ('jack',20),('rose',21),
                                        ('jone',15);

end $


//调用无参存储过程,因为我们没有在最后做delimiter ;的声明,所以mysql的结束符现在还是$,若声明了就写成call mysql();
call mysql()$

 1.5.带in模式的存储过程

带in模式的存储过程中,关键字in可以省略,但不建议这么做。具体形式为:(in 参数名 参数类型)或直接(参数名 参数类型)

还是以person表为例,现在查询表中年龄为20或13的人?

//创建存储过程
delimiter $

create procedure mysql2(in nianling int(5),in nianling2 int(5))

begin

    select p.* from person p where age=nianling or age=nianling2;

end $

delimiter ;

//调用存储过程
call mysql2(20,13);

1.6.带out模式的存储过程

带out模式存储过程的具体形式为:(out 参数名 参数类型)

以person表为例,现在查询表中年龄为20的人的姓名叫什么?

//创建存储过程
delimiter $

create procedure mysql3(in nianling int(5),out xingming varchar(20))

begin
    //将查询得到的结果直接通过into赋值给输出变量xingming

    select p.name into xingming from person p where age=nianling;

end $

delimiter ;

//调用存储过程,第二个参数的名称可以随意写,它只是作为一个接收查询返回结果的一个容器
call mysql3(20,name);
//执行这句话后,就可以打印出年龄为20的人的具体姓名了,这里的name要与调用时传入的第二个参数保持名称一致
select name;

1.7.带inout模式的存储过程

带inout模式存储过程的具体形式为:(inout 参数名 参数类型)

案例:传入两个值a和b,最终让a和b都翻倍并返回。

//先定义两个变量的值,方便一会儿在调用存储过程的时候进行传入
set m=10;
set n=20;
//创建存储过程
delimiter $

create procedure mysql4(inout a int(5),inout b int(5))

begin

    //存储过程中为变量赋值也需要用到关键字set
    set a=a*2;
    set b=b*2;

end $

delimiter ;

//调用存储过程
call mysql4(m,n);
//查询调用存储过程后m和n的值,为20和40
select m,n;

1.8.参看存储过程的具体信息以及如何删除存储过程

参看语法show create procedure 存储过程名;   如:show create procedure mysql4;

删除语法:drop procedure 存储过程的名称;        如:drop procedure mysql4;

注意:存储过程的删除只能一个一个的来,不可批量删除。

二、存储函数

存储函数和存储过程非常的类似,具体二者的区别我会在第三部分详细介绍。这里我们先看存储函数。

2.1存储函数的定义格式

delimiter $
create function function_name(参数列表) returns type

begin

    函数体
    return 返回值;

end
delimiter ;

关于存储函数的定义需要注意几点点:

  ①参数列表:参数列表包含两个部分——参数名和参数类型

  ②函数体:函数体中必须有return(注意函数体里是return不是returns)语句,如果没有会报错。return语句不放在函数体的最后也不会报错,但不建议这么做。

  ③如果函数体中仅有一句话,则可以省略begin和end关键字

  ④同存储过程一样,通过delimiter去设置存储函数的结束标记

  ⑤returns type(注意是returns不是return)必须在创建存储函数的时候就写上,type表示返回值得类型,比如返回的是int类型,则写为returns int

2.2存储函数的调用格式

存储函数可以直接放到某个SQL语句中进行调用

2.3存储函数案例演示:有参有返回值

根据姓名返回其对应的年龄

delimiter $
create function fun1(names varchar(20)) returns int
begin
    #定义一个变量nianling用于接收查询出来的结果并返回
    set nianling = 0;
    #将查询出来的年龄赋值给定义好的标量nianling
    select age into nianling from person where name = names;
    #返回结果
    return nianling;
end $
delimiter ;

#调用存储函数fun1
select fun1('tom');

2.4存储函数案例演示:无参有返回值

返回person表中现有的人数个数:

delimiter $
create function fun2() returns int
begin
    #声明一个int类型的变量num,默认值为0
    declare num int default 0;
    #将查询结果赋值给num
    select count(*) into num from person;
    #返回结果
    return num;
end $
delimiter ;

#调用存储函数fun2
select fun2();

2.5参看存储函数的具体信息以及如何删除存储函数

参看语法show create function 存储函数名;   如:show create function fun1;

删除语法:drop function 存储函数名;       如:drop function fun1;

注意:存储函数的删除只能一个一个的来,不可批量删除。

三、存储过程和存储函数的区别

通过上面对存储过程和存储函数的学习我们可以看出他两非常的相识,但他们是有区别的,具体的应用场景也不同。存储函数的限制比较多,例如不能用临时表,只能用表变量,而存储过程的限制较少,存储过程的实现功能要复杂些,而函数的实现功能“针对性”比较强。下面简单的从四个方面说说二者的区别。

1.返回值不同:

    存储过程可以没有返回,也可以有多个返回,它的返回值是通过out或inout参数来传递的

    而存储函数必须且有且只能有一个返回,它的返回值是通过return语句返回的

2.调用时机不同

    存储过程通过call语句调用

    存储函数嵌入在SQL中使用

3.参数不同

    存储过程的参数类型有in、out、inout,当然参数也可以为空

    存储函数的参数可以为空,也可以不为空,不为空的时候类似于存储过程的in类型(但不需要带关键字in),只能作为输入。

4.应用场景不同

    存储过程:批量插入、批量修改等相对较复杂的操作

    存储函数:适合在某些SQL语句中嵌入某个存储函数进行一些数据处理进而返回“一个”值来结合整个SQL语句来做相关的业务处理。

四、案例实战

通过上面的介绍,我们接下来通过一个具体的小案例来运用一下存储函数和存储过程。具体需求是:往一张只有两个字段id和姓名的员工表emp中插入十万条数据。

1.先把表建起来:

create table emp(
    eid int(5) primary key,
    ename varchar(20) not null default ''
)engine=innodb default charset=utf8;

这里我们要求员工姓名是由6个随机字母组成的字符串。这个随机字符串我们就通过“存储函数”来生成。

2.创建产生随机字符串的存储函数

通过存储函数产生随机字符串,用这个随机字符串表示姓名

delimiter $
create function randName(n int) returns varchar(20)
begin
    declare all_str varchar(100) default 'abcdefghijklmnopqrstuvwxyz';
    declare return_str varchar(20) default '';
    declare i int default 0;
    while i<n
    do
        set return_str = concat(return_str,substring(all_str,FLOOR(1+rand()*26),1));
        set i=i+1;
    end while;
    return return_str;
end $
delimiter ;

针对上面的这个存储函数我们来详细说明一下:

  • 首先我们创建了一个名为 randName(n int)的存储函数,它的作用就是产生一个6为数的随机字符串,参数n就是要指定的生成字符串的位数,比如我们要生成6位,那么在调用这个函数的时候就在里面传入6即可。
  • 接着我们申明了一个默认值是26个英文字母的变量all_str,它的作用就是我们在生成随机字符串的时候要用到的源数据。即从这26个字母中一个个的取出字符(每次取一个,取六次共取到6个),然后拼接成一个随机的6位字符串。
  • return_str这个变量就是我们最终要返回的拼接好的6为随机字符串,刚开始我们先给它指定为空。
  • i这个变量是我们在循环时要用到的,就是和n比较,如果i小于n,循环就一直进行
  • 接下来就是while循环体了,循环体的具体内容要包裹在do/end while中。
  • 接下来就是循环体中的具体内容了:首先concat()函数是mysql给我们提供的,专门用于拼接字符串。它里面有个两个参数,第一个是待拼接的字符串(这里我们定义了return_str且指定其最开始的默认值是空字符串);第二个参数是要拼接的具体字符串,这个我们这里涉及到三个函数我会在下面展开讲一下,你现在只需知道这一长串的东西最后的结果就是从哪26个字符串中截取了其中的一个。
  • 接着看concat()函数中第二个参数的具体内容:首先substring()函数也是mysql给我们提供的专门用于截取字符串的函数,它里面有三个参数,第一个是待截取的字符串,第二个是表示从待截取的字符串中的那位开始截取(默认第一位是1不是0),第三个参数是表示要截取几位。比如substring('abcde',2,1)就表示从字符串abcde的第二位开始截取,只截取1位,那么最后的截取结果就是b。而我们这里要截取的就是在上面定义好的26个字母all_str,截取的位数是1位,而具体从这26个字母的那位开始截呢,这就是要用第二个参数来指定了。
  • FLOOR(rand()*26)+1),这就是substring()中的第二个参数,最外层的FOOLR()函数是一个向下取整的函数,比如FLOOR(2.999)向下取整后的结果就是2。rand()函数也是mysql提供给我们的一个函数,它的作用就是产生一个0到1的随机数(注意这个随机数包括0但不包过1,即左闭右开[0,1))。那么rand()*26的结果就是[0,26)之间的某个数,为了能把那26个字符串的第一位也能取到我们给这个随机结果加1,最后通过foolr()函数向下取整,那么最终FLOOR(rand()*26)+1)得到的结果就是1-26之间的一个随机数。
  • 循环体中的set i = i + 1;就相当于我们java中的i++;没循环一次让i加一,知道不满足循环条件后跳出循环。
  • 最后把拼接好的字符你串return_str返回

如果你在输入上面的语句并执行后报如上图的错误,是因为存储过程/函数在创建时,与慢查询日志冲突了。具体来说就是你在mysql中开启了慢查询日志后,如果再去创建存储过程/函数就会报这个错误,具体什么是慢查询日志如果有机会我会单写一篇博客来讲,这里你只需要简单的理解为mysql中的一种日志记录机制就好。

我们先通过命令产看一下:show variables like '%log_bin_trust_function_creators%';

看到后面那个value值为off的话,在创建存储函数/过程时就会报上面的错误,现在我们通过set global log_bin_trust_function_creators=1;把它开启即可

这是我们再输入上面的存储函数即可执行成功。

3.创建存储过程

我们通过存储过程来插入海量数据到emp表中

delimiter $
create procedure insert_emp(in eid_start int(10),in data_times int(10))
begin
    declare i int default 0 ;
    set autocommit=0;
    repeat
        insert into emp values(eid_start+i,randName(6));
        set i=i+1;
        until i=data_times
    end repeat;
    commit;
end $
delimiter ;

针对上面的存储过程我们在做一下具体的分析:

  • 首先创建了一个名为insert_emp()的存储过程,它的作用就是往emp表中插入海量数据。我们为它设置了输入参数eid_startdata_times,第一个表示主键eid从什么时候开始,第二个参数可以理解为往emp表中插入几条数据,比如insert_emp(1,100),那么就表示在往emp表中插入数据的过程中,主键从1开始算起,一值插入100条数据结束,那么最后一条数据的主键值就为101。
  • set autocommit=0;表示我们把自动提交关闭了,否则在调用这个存储过程的时候我们没插入一条数据系统就给我们自动提交一次,假如插入10万条就要自动提交10万次,这样是非常损耗性能的,所以我们干脆把自动提交关闭了,在10万条数据都完成插入后再统一的手动提交。
  • repeat/end repeat;这个也表示一个循环体,也是mysql中的语法,作用同上面将的while一样。循环体中的第一个SQL语句就是具体的插入语句了,可以看到它的主键值由我们在调用存储过程insert_emp()的时候由里面的参数eid_start指定,并随着i的不断增加,主键也不断的增加,后面的randName()就是我们在上面定义的存储函数,用于产生6位随机字符串表示员工的姓名。
  • set i = i + 1;类似java中的i++
  • until i=data_times循环结束的条件,注意它的后面没有分号。当i的值累加到data_times的时候循环结束。

  • commit;10万条数据插入完毕后,我们手动提交。

4.调用存储过程

现在存储函数和存储过程都已经创建好了,我们现在就通过调用存储过程来实现向emp表中插入10万条数据的目的:

call insert_emp(1,100000);

可以看到我在我的电脑上插入10条数据用了6.6秒。

我们通过select * from emp;可以查看一下具体的数据:

可以看到id确实是从1到100000,而姓名则是由6个随机的字符拼接而成。

关于存储过程和存储函数就讲到这里,谢谢大家的耐心观看。

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值