面试官突然问我MySQL存储过程,我竟然连基础都不会!(详细)

MySQL存储过程

一、存储过程

1.1 什么是存储过程

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。在数据量特别庞大的情况下利用存储过程能达到倍速的效率提升

1.2 数据库存储过程程序

当我们了了解存储过程是什么之后,就需要了解数据库中存在的这三种类型的数据库存储类型程序,如下:

存储过程: 存储过程是最常见的存储程序,存储过程是能够接受输入和输出参数并且能够在请求时被执行的程序单元。

存储函数: 存储函数和存储过程很相像,但是它的执行结果会返回一个值。最重要的是存储函数可以被用来充当标准的 SQL 语句,允许程序员有效的扩展 SQL 语言的能力。

触发器: 触发器是用来响应激活或者触发数据库行为事件的存储程序。通常,触发器用来作为数据库操作语言的响应而被调用,触发器可以被用来作为数据校验和自动反向格式化。

注意: 其他的数据库提供了别的数据存储程序,包括包和类。目前MySQL不提供这种结构。

1.3 为什么要使用存储程序

虽然目前的开发中存储程序我们使用的并不是很多,但是不一定就否认它。其实存储程序会为我们使用和管理数据库带来了很多优势:

使用存储程序更加安全。

存储程序提供了一种数据访问的抽象机制,它能够极大的改善你的代码在底层数据结构演化过程中的易维护性。

存储程序可以降低网络拥阻,因为属于数据库服务器的内部数据,这相比在网上传输数据要快的多。

存储程序可以替多种使用不同构架的外围应用实现共享的访问例程,无论这些构架是基于数据库服务器外部还是内部。

以数据为中心的逻辑可以被独立的放置于存储程序中,这样可以为程序员带来更高、更为独特的数据库编程体验。

在某些情况下,使用存储程序可以改善应用程序的可移植性。(在另外某些情况下,可移植性也会很差!)

这里我大致解释一下上述几种使用存储程序的优势:

我们要知道在Java语言中,我们使用数据库与Java代码结合持久化存储需要引入JDBC来完成。会想到JDBC,我们是否还能想起SQL注入问题呢?虽然使用PreparedStatement解决SQL注入问题,那就真的是绝对安全吗?不,它不是绝对安全的。

这时候分析一下数据库与Java代码的连接操作流程。在BS结构中,一般都是浏览器访问服务器的,再由服务器发送SQL语句到数据库,在数据库中对SQL语句进行编译运行,最后把结果通过服务器处理再返回浏览器。在此操作过程中,浏览器对服务器每发送一次对数据库操作的请求就会调用对应的SQL语句编译和执行,这是一件十分浪费性能的事情, 性能下降了就说明对数据库的操作 效率低 了。

还有一种可能是,在这个过程中进行发送传输的SQL语句是对真实的库表进行操作的SQL语句,如果在发送传输的过程中被拦截了,一些不法分子会根据他所拦截的SQL语句推断出我们数据库中的库表结构,这是一个很大的 安全隐患 。

关于可维护性的提高,这里模拟一个场景。通常数据库在公司中是由DBA来管理的,如果管理数据库多年的DBA辞职了,此时数据库会被下一任DBA来管理。这里时候问题来了,数据库中这么多的数据和SQL语句显然对下一任管理者不太友好。就算管理多年的DBA长时间不操作查看数据库也会忘记点什么东西。所以,我们在需要引入存储程序来进行SQL语句的统一编写和编译, 为维护提供了便利 。(其实我觉得这个例子并不生动合理,但是为了大家能理解,请体谅!)

讲了很多存储程序的优势演变过程,其核心就是:需要将编译好的一段或多段SQL语句放置在数据库端的存储程序中,以便解决以上问题并方便开发者直接调用。

二、存储过程的使用步骤

2.1 存储过程的开发思想

存储过程时数据库的一个重要的对象,可以封装SQL语句集,可以用来完成一些较复杂的业务逻辑,并且可以入参(传参)、出参(返回参数),这里与Java中封装方式十分相似。

而且创建时会预先编译后保存,开发者后续的调用都不需要再次编译。

2.2 存储过程的优缺点

存储过程使用的优缺点其实在1.3中的优势中说到了。这里我简单罗列一下存储过程的优点与缺点。

优点:

在生产环境下,可以通过直接修改存储过程的方式修改业务逻辑或bug,而不用重启服务器。

执行速度快,存储过程经过编译之后会比单独一条一条编译执行要快很多。

减少网络传输流量。

便于开发者或DBA使用和维护。

在相同数据库语法的情况下,改善了可移植性。

缺点:

过程化编程,复杂业务处理的维护成本高。

调试不便。

因为不同数据库语法不一致,不同数据库之间可移植性差。

2.3 MySQL存储过程的官方文档

英语好或者有能力的小伙伴可以去参考一下官方文档。如果不参考官方文档,没关系,我在下面也会详细讲述MySQL存储过程的各个知识点。

1https://dev.mysql.com/doc/refman/5.6/en/preface.html

2.3 存储过程的使用语法

1createPROCEDURE 过程名( in|out|inout 参数名 数据类型 , ...)2begin3sql语句;4end;5call过程名(参数值);

in 是定义传入参数的关键字。 out 是定义出参的关键字。 inout 是定义一个出入参数都可以的参数。如果括号内什么都不定义,就说明该存储过程时一个无参的函数。在后面会有详细的案例分析。

注意:SQL语句默认的结束符为 ; ,所以在使用以上存储过程时,会报1064的语法错误。我们可以使用 DELIMITER 关键字临时声明修改SQL语句的结束符为 // ,如下:

1-- 临时定义结束符为"//"2DELIMITER//3create PROCEDURE 过程名(in|out参数名 数据类型 , ...)4begin5sql语句;6end//7-- 将结束符重新定义回结束符为";"8DELIMITER ;

例如:使用存储过程来查询员工的工资(无参)

注意:如果在特殊的必要情况下,我们还可以通过 delimiter 关键字将 ; 结束符声明回来使用,在以下案例中我并没有这样将结束符声明回原来的 ; ,在此请大家注意~

为什么我在这里提供了drop(删除)呢?

是因为我们在使用的时候如果需要修改存储过程中的内容,我们需要先删除现有的存储过程后,再creat重新创建。

1# 声明结束符为//2delimiter//34# 创建存储过程(函数)5createprocedurese()6begin7selectsalaryfromemployee;8end//910# 调用函数11callse()//1213# 删除已存在存储过程——se()函数14dropprocedureifexistsse//

三、存储过程的变量和赋值

3.1 局部变量

声明局部变量语法: declare var_name type [default var_value];

赋值语法:

注意:局部变量的定义,在begin/end块中有效。

使用set为参数赋值

1# set赋值23# 声明结束符为//4delimiter//56# 创建存储过程7createprocedureval_set()8begin9# 声明一个默认值为unknown的val_name局部变量10declareval_namevarchar(32)default'unknown';11# 为局部变量赋值12setval_name='Centi';13# 查询局部变量14selectval_name;15end//1617# 调用函数18callval_set()//19

使用into接收参数

1delimiter // 2create procedure val_into() 3begin 4# 定义两个变量存放name和age5declareval_namevarchar(32)default'unknown'; 6declareval_ageint; 7# 查询表中id为1的name和age并放在定义的两个变量中8selectname,ageintoval_name,val_agefromemployeewhereid=1; 9# 查询两个变量10selectval_name,val_age;11end //1213call val_into() //14

3.2 用户变量

用户自定义用户变量,当前会话(连接)有效。与Java中的成员变量相似。

语法: @val_name

注意: 该用户变量不需要提前声明,使用即为声明。

1delimiter//2createprocedureval_user()3begin4# 为用户变量赋值5set@val_name='Lacy';6end//78# 调用函数9callval_user()//1011# 查询该用户变量12select@val_name//

3.3 会话变量

会话变量是由系统提供的,只在当前会话(连接)中有效。

语法: @@session.val_name

1# 查看所有会话变量2showsessionvariables;3# 查看指定的会话变量4select@@session.val_name;5# 修改指定的会话变量6set@@session.val_name=0;

这里我获取了一下所有的会话变量,大概有500条会话变量的记录。等我们深入学习MySQL后,了解了各个会话变量值的作用,可以根据需求和场景来修改会话变量值。

1delimiter//2createprocedure val_session()3begin4# 查看会话变量5show session variables;6end//78callval_session() //9

image-20200610112512964

3.4 全局变量

全局变量由系统提供,整个MySQL服务器内有效。

语法: @@global.val_name

1# 查看全局变量中变量名有char的记录2showglobalvariables like'%char%'//3# 查看全局变量character_set_client的值4select@@global.character_set_client//

3.5 入参出参

入参出参的语法我们在文章开头已经提过了,但是没有演示,在这里我将演示一下入参出参的使用。

语法: in|out|inout 参数名 数据类型 , ...

in 定义出参; out 定义入参; inout 定义出参和入参。

出参in

使用出参in时,就是需要我们传入参数,在这里可以对参入的参数加以改变。简单来说in只负责传入参数到存储过程中,类似Java中的形参。

1delimiter//2createprocedureval_in(inval_namevarchar(32))3begin4# 使用用户变量出参(为用户变量赋参数值)5set@val_name1=val_name;6end//78# 调用函数9callval_in('DK')//1011# 查询该用户变量12select@val_name1//

入参out

在使用out时,需要传入一个参数。而这个参数相当于是返回值,可以通过调用、接收来获取这个参数的内容。简单来说out只负责作返回值。

1delimiter//2# 创建一个入参和出参的存储过程3create procedureval_out(inval_idint,outval_name varchar(32)) 4begin 5    # 传入参数val_id查询员工返回name值(查询出的name值用出参接收并返回) 6selectnameintoval_namefromemployeewhereid= val_id;7end//89# 调用函数传入参数并声明传入一个用户变量10callval_out(1, @n)//1112# 查询用户变量13select @n//

入参出参inout

inout关键字,就是把in和out合并成了一个关键字使用。被关键字修饰的参数既可以出参也可以入参。

1delimiter // 2create procedure val_inout(in val_name varchar(32), inout val_age int) 3begin 4# 声明一个a变量5declareaint; 6# 将传入的参数赋值给a变量7seta = val_age; 8# 通过name查询age并返回val_age9selectageintoval_agefromemployeewherename= val_name;10# 将传入的a与-和查询age结果字符串做拼接并查询出来(concat——拼接字符串)11selectconcat(a,'-', val_age);12end //1314# 声明一个用户变量并赋予参数为4015set @ages = '40' //16# 调用函数并传入参数值17call val_inout('Ziph', @ages) //18# 执行结果19# 40-18

四、存储过程中的流程控制

4.1 if 条件判断(推荐)

扩展: timestampdiff(unit, exp1, exp2) 为exp2 - exp1得到的差值,而单位是unit。(常用于日期)

扩展例子: select timestampdiff(year,’2020-6-6‘,now()) from emp e where id = 1;

解释扩展例子:查询员工表中id为1员工的年龄,exp2就可以为该员工的出生年月日,并以年为单位计算。

语法:

1IF条件判断 THEN 结果2[ELSEIF 条件判断 THEN 结果] ...3[ELSE 结果]4ENDIF

举例:传入所查询的id参数查询工资标准(s<=6000为低工资标准;6000 <=10000为中工资标准;10000 <=15000为中上工资标准;s style="font-size: inherit; color: inherit; line-height: inherit; margin: 0px; padding: 0px;">=15000为高工资标准)

1delimiter//2createprocedures_sql(inval_idint)3begin4# 声明一个局部变量result存放工资标准结果5declareresultvarchar(32);6# 声明一个局部变量存放查询得到的工资7declaresdouble;8# 根据入参id查询工资9selectsalaryintosfromemployeewhereid=val_id;10# if判断的使用11ifs<=6000then12setresult='低工资标准';13elseifs<=10000then14setresult='中工资标准';15elseifs<=15000then16setresult='中上工资标准';17else18setresult='高工资标准';19endif;20# 查询工资标准结果21selectresult;22end//2324# 调用函数,传入参数25calls_sql(1);

4.2 case条件判断

关于case语句,不仅仅在存储过程中可以使用,MySQL基础查询语句中也有用到过。相当于是Java中的switch语句。

语法:

1# 语法一2CASEcase_value3WHENwhen_valueTHEN结果4[WHENwhen_valueTHEN结果]...5[ELSE结果]6ENDCASE78# 语法二(推荐语法)9CASE10WHEN条件判断THEN结果11[WHEN条件判断THEN结果]...12[ELSE结果]13ENDCASE

举例:

1# 语法一2delimiter//3create procedures_case(inval_idint) 4begin 5    # 声明一个局部变量result存放工资标准结果 6    declare resultvarchar(32);7# 声明一个局部变量存放查询得到的工资8declare sdouble;9# 根据入参id查询工资10selectsalaryintosfromemployeewhereid = val_id;11cases12when6000thensetresult ='低工资标准';13when10000thensetresult ='中工资标准';14when15000thensetresult ='中上工资标准';15elsesetresult ='高工资标准';16endcase;17selectresult;18end//1920calls_case(1);2122# 语法二(推荐)23delimiter//24create procedures_case(inval_idint)25begin26    # 声明一个局部变量result存放工资标准结果27    declare resultvarchar(32);28# 声明一个局部变量存放查询得到的工资29declare sdouble;30# 根据入参id查询工资31selectsalaryintosfromemployeewhereid = val_id;32case33whens <=6000thensetresult ='低工资标准';34whens <=10000thensetresult ='中工资标准';35whens <=15000thensetresult ='中上工资标准';36elsesetresult ='高工资标准';37endcase;38selectresult;39end//4041calls_case(1);

4.3 loop循环

loop为死循环,需要手动退出循环,我们可以使用 leave 来退出循环

可以把leave看成Java中的break;与之对应的,就有 iterate (继续循环)也可以看成Java的continue

语法:

1[别名:]LOOP2    循环语句3ENDLOOP[别名]

注意:别名和别名控制的是同一个标签。

示例1:循环打印1~10(leave控制循环的退出)

注意:该loop循环为死循环,我们查的1~10数字是i,在死循环中设置了当大于等于10时停止循环,也就是说先后执行了10次该循环内的内容,结果查询了10次,生成了10个结果(1~10)。

1delimiter//2createprocedures_loop()3begin4# 声明计数器5declareiintdefault1;6# 开始循环7    num:8loop9# 查询计数器记录的值10selecti;11# 判断大于等于停止计数12ifi>=10then13leavenum;14endif;15# 计数器自增116seti=i+1;17# 结束循环18endloopnum;19end//2021calls_loop();

打印结果:

image-20200610191639524

示例2:循环打印1~10(iterate和leave控制循环)

注意:这里我们使用字符串拼接计数器结果,而条件如果用iterate就必须时 i < 10 了!

1delimiter//2createprocedures_loop1()3begin4# 声明变量i计数器5declareiintdefault1;6# 声明字符串容器7declarestrvarchar(256)default'1';8# 开始循环9    num:10loop11# 计数器自增112seti=i+1;13# 字符串容器拼接计数器结果14setstr=concat(str,'-',i);15# 计数器i如果小于10就继续执行16ifi<10then17iteratenum;18endif;19# 计数器i如果大于10就停止循环20leavenum;21# 停止循环22endloopnum;23# 查询字符串容器的拼接结果24selectstr;25end//2627calls_loop1();

image-20200610193153512

4.4 repeat循环

repeat循环类似Java中的do while循环,直到条件不满足才会结束循环。

语法:

1[别名:]REPEAT2    循环语句3UNTIL 条件4END REPEAT[别名]

示例:循环打印1~10

1delimiter//2createprocedures_repeat()3begin4declareiintdefault1;5declarestrvarchar(256)default'1';6# 开始repeat循环7    num:8repeat9seti=i+1;10setstr=concat(str,'-',i);11# until 结束条件12# end repeat 结束num 结束repeat循环13untili>=10endrepeatnum;14# 查询字符串拼接结果15selectstr;16end//1718calls_repeat();

4.5 while循环

while循环就与Java中的while循环很相似了。

语法:

1[别名]WHILE条件DO2循环语句3ENDWHILE[别名]

示例:循环打印1~10

1delimiter//2createprocedures_while()3begin4declareiintdefault1;5declarestrvarchar(256)default'1';6# 开始while循环7    num:8# 指定while循环结束条件9whilei<10do10seti=i+1;11setstr=concat(str,'+',i);12# while循环结束13endwhilenum;14# 查询while循环拼接字符串15selectstr;16end//1718calls_while();

4.6 流程控制语句(继续、结束)

至于流程控制的继续和结束,我们在前面已经使用过了。这里再列举一下。

leave:与Java中break;相似

1leave标签;

iterate:与Java中的continue;相似

1iterate 标签;

五、游标与handler

5.1 游标

游标是可以得到某一个结果集并逐行处理数据。游标的逐行操作,导致了游标很少被使用!

语法:

1DECLARE游标名 CURSOR FOR 查询语句2--打开语法3OPEN游标名4--取值语法5FETCH游标名 INTO var_name [, var_name] ...6--关闭语法7CLOSE游标名

了解了游标的语法,我们开始使用游标。如下:

示例:使用游标查询id、name和salary。

1delimiter//2createproceduref()3begin4declareval_idint;5declareval_namevarchar(32);6declareval_salarydouble;78# 声明游标9declareemp_flagcursorfor10selectid,name,salaryfromemployee;1112# 打开13openemp_flag;1415# 取值16fetchemp_flagintoval_id,val_name,val_salary;1718# 关闭19closeemp_flag;2021selectval_id,val_name,val_salary;22end//2324callf();

执行结果:

image-20200610203622749

因为游标逐行操作的特点,导致我们只能使用游标来查询一行记录。怎么改善代码才可以实现查询所有记录呢?聪明的小伙伴想到了使用循环。对,我们试试使用一下循环。

1delimiter//2createproceduref()3begin4declareval_idint;5declareval_namevarchar(32);6declareval_salarydouble;78# 声明游标9declareemp_flagcursorfor10selectid,name,salaryfromemployee;1112# 打开13openemp_flag;1415# 使用循环取值16c:loop17# 取值18fetchemp_flagintoval_id,val_name,val_salary;19endloop;2021# 关闭22closeemp_flag;2324selectval_id,val_name,val_salary;25end//2627callf();

image-20200610204034224

我们使用循环之后,发现有一个问题,因为循环是死循环,我们不加结束循环的条件,游标会一直查询记录,当查到没有的记录的时候,就会抛出异常 1329:未获取到选择处理的行数 。

如果我们想办法指定结束循环的条件该怎么做呢?

这时候可以声明一个boolean类型的标记。如果为true时则查询结果集,为false时则结束循环。

1delimiter//2createproceduref()3begin4declareval_idint;5declareval_namevarchar(32);6declareval_salarydouble;78# 声明flag标记9declareflagbooleandefaulttrue;1011# 声明游标12declareemp_flagcursorfor13selectid,name,salaryfromemployee;1415# 打开16openemp_flag;1718# 使用循环取值19c:loop20fetchemp_flagintoval_id,val_name,val_salary;21# 如果标记为true则查询结果集22ifflagthen23selectval_id,val_name,val_salary;24# 如果标记为false则证明结果集查询完毕,停止死循环25else26leavec;27endif;28endloop;2930# 关闭31closeemp_flag;3233selectval_id,val_name,val_salary;34end//3536callf();

上述代码你会发现并没有写完,它留下了一个很严肃的问题。当flag = false时候可以结束循环。但是什么时候才让flag为false啊?

于是,MySQL为我们提供了一个 handler 句柄。它可以帮我们解决此疑惑。

handler句柄语法: declare continue handler for 异常 set flag = false;

handler句柄可以用来捕获异常,也就是说在这个场景中当捕获到 1329:未获取到选择处理的行数 时,就将flag标记的值改为false。这样使用handler句柄就解决了结束循环的难题。让我们来试试吧!

终极版示例:解决了多行查询以及结束循环问题。

1delimiter//2createproceduref()3begin4declareval_idint;5declareval_namevarchar(32);6declareval_salarydouble;78# 声明flag标记9declareflagbooleandefaulttrue;1011# 声明游标12declareemp_flagcursorfor13selectid,name,salaryfromemployee;1415# 使用handler句柄来解决结束循环问题16declarecontinuehandlerfor1329setflag=false;1718# 打开19openemp_flag;2021# 使用循环取值22c:loop23fetchemp_flagintoval_id,val_name,val_salary;24# 如果标记为true则查询结果集25ifflagthen26selectval_id,val_name,val_salary;27# 如果标记为false则证明结果集查询完毕,停止死循环28else29leavec;30endif;31endloop;3233# 关闭34closeemp_flag;3536selectval_id,val_name,val_salary;37end//3839callf();

执行结果:

image-20200610210925964

在执行结果中,可以看出查询结果以多次查询的形式,分布显示到了每一个查询结果窗口中。

注意:在语法中,变量声明、游标声明、handler声明是必须按照先后顺序书写的,否则创建存储过程出错。

5.2 handler句柄

语法:

1DECLAREhandler操作 HANDLER2FOR 情况列表...(比如:异常错误情况)3操作语句

注意:异常情况可以写异常错误码、异常别名或SQLSTATE码。

handler操作:

CONTINUE: 继续

EXIT: 退出

UNDO: 撤销

异常情况列表:

mysql_error_code

SQLSTATE [VALUE] sqlstate_value

condition_name

SQLWARNING

NOT FOUND

SQLEXCEPTION

注意:MySQL中各种异常情况代码、错误码、别名和SQLSTATEM码可参考官方文档:

https://dev.mysql.com/doc/refman/5.6/en/server-error-reference.html

写法示例:

1DECLAREexitHANDLERFORSQLSTATE'3D000'setflag =false;2DECLAREcontinueHANDLERFOR1050setflag =false;3DECLAREcontinueHANDLERFORnotfoundsetflag =false;

六、循环创建表

需求:创建下个月的每天对应的表,创建的表格式为: comp_2020_06_01、comp_2020_06_02、...

描述:我们需要用某个表记录很多数据,比如记录某某用户的搜索、购买行为(注意,此处是假设用数据库保存),当每天记录较多时,如果把所有数据都记录到一张表中太庞大,需要分表,我们的要求是,每天一张表,存当天的统计数据,就要求提前生产这些表——每月月底创建下一个月每天的表!

预编译: PREPARE 数据库对象名 FROM 参数名

执行: EXECUTE 数据库对象名 [USING @var_name [, @var_name] ...]

通过数据库对象创建或删除表: {DEALLOCATE | DROP} PREPARE 数据库对象名

关于时间处理的语句:

1--EXTRACT(unit FROM date)              截取时间的指定位置值2--DATE_ADD(date,INTERVAL expr unit)    日期运算3--LAST_DAY(date)                          获取日期的最后一天4--YEAR(date)                            返回日期中的年5--MONTH(date)                            返回日期的月6--DAYOFMONTH(date)                        返回日

代码:

1--思路:循环构建表名comp_2020_06_01到comp_2020_06_30;并执行create语句。2delimiter//3createproceduresp_create_table()4begin5# 声明需要拼接表名的下一个月的年、月、日6declarenext_yearint;7declarenext_monthint;8declarenext_month_dayint;910# 声明下一个月的月和日的字符串11declarenext_month_strchar(2);12declarenext_month_day_strchar(2);1314# 声明需要处理每天的表名15declaretable_name_strchar(10);1617# 声明需要拼接的118declaret_indexintdefault1;19# declare create_table_sql varchar(200);2021# 获取下个月的年份22setnext_year=year(date_add(now(),INTERVAL1month));23# 获取下个月是几月 24setnext_month=month(date_add(now(),INTERVAL1month));25# 下个月最后一天是几号26setnext_month_day=dayofmonth(LAST_DAY(date_add(now(),INTERVAL1month)));2728# 如果下一个月月份小于10,就在月份的前面拼接一个029ifnext_month<1030thensetnext_month_str=concat('0',next_month);31else32# 如果月份大于10,不做任何操作33setnext_month_str=concat('',next_month);34endif;3536# 循环操作(下个月的日大于等于1循环开始循环)37whilet_index<=next_month_daydo3839# 如果t_index小于10就在前面拼接040if(t_index<10)41thensetnext_month_day_str=concat('0',t_index);42else43# 如果t_index大于10不做任何操作44setnext_month_day_str=concat('',t_index);45endif;4647# 拼接标命字符串48settable_name_str=concat(next_year,'_',next_month_str,'_',next_month_day_str);49# 拼接create sql语句50set@create_table_sql=concat(51'create table comp_',52table_name_str,53'(`grade` INT(11) NULL,`losal` INT(11) NULL,`hisal` INT(11) NULL) COLLATE=\'utf8_general_ci\' ENGINE=InnoDB');54# 预编译55# 注意:FROM后面不能使用局部变量!56preparecreate_table_stmtFROM@create_table_sql;57# 执行58executecreate_table_stmt;59# 创建表60DEALLOCATEpreparecreate_table_stmt;6162# t_index自增163sett_index=t_index+1;6465endwhile;66end//6768# 调用函数69callsp_create_table()

七、其他

7.1 characteristic

在MySQL存储过程中,如果没有显示的定义characteristic,它会隐式的定义一系列特性的默认值来创建存储过程。

LANGUAGE SQL

存储过程语言,默认是sql,说明存储过程中使用的是sql语言编写的,暂时只支持sql,后续可能会支持其他语言

NOT DETERMINISTIC

是否确定性的输入就是确定性的输出,默认是NOT DETERMINISTIC,只对于同样的输入,输出也是一样的,当前这个值还没有使用

CONTAINS SQL

提供子程序使用数据的内在信息,这些特征值目前提供给服务器,并没有根据这些特征值来约束过程实际使用数据的情况。有以下选择:CONTAINS SQL表示子程序不包含读或者写数据的语句NO SQL 表示子程序不包含sqlREADS SQL DATA 表示子程序包含读数据的语句,但是不包含写数据的语句MODIFIES SQL DATA 表示子程序包含写数据的语句。

SQL SECURITY DEFINER

MySQL存储过程是通过指定SQL SECURITY子句指定执行存储过程的实际用户。所以次值用来指定存储过程是使用创建者的许可来执行,还是执行者的许可来执行,默认值是DEFINERDEFINER 创建者的身份来调用,对于当前用户来说:如果执行存储过程的权限,且创建者有访问表的权限,当前用户可以成功执行过程的调用的INVOKER 调用者的身份来执行,对于当前用户来说:如果执行存储过程的权限,以当前身份去访问表,如果当前身份没有访问表的权限,即便是有执行过程的权限,仍然是无法成功执行过程的调用的。

COMMENT ''

存储过程的注释性信息写在COMMENT里面,这里只能是单行文本,多行文本会被移除到回车换行等

7.2 死循环处理

如有死循环处理,可以通过下面的命令查看并杀死(结束)

1showprocesslist;2killid;

7.3 select语句中书写case

1select2case3when 条件判断 then 结果4when 条件判断 then 结果5else 结果6end 别名,7*8from表名;

7.4 复制表和数据

1CREATE TABLE deptSELECT*FROMprocedure_demo.dept;2CREATE TABLE empSELECT*FROMprocedure_demo.emp;3CREATE TABLE salgradeSELECT*FROMprocedure_demo.salgrade;

7.5 临时表

1create temporary table 表名( 2  字段名 类型 [约束], 3  name varchar(20)  4)Engine=InnoDB default charset utf8; 5 6-- 需求:按照部门名称查询员工,通过select查看员工的编号、姓名、薪资。(注意,此处仅仅演示游标用法)7delimiter $$ 8create procedure sp_create_table02(in dept_name varchar(32)) 9begin10declareemp_noint;11declareemp_namevarchar(32);12declareemp_saldecimal(7,2);13declareexit_flagintdefault0;1415declareemp_cursorcursorfor16selecte.empno,e.ename,e.sal17fromemp einnerjoindept done.deptno = d.deptnowhered.dname = dept_name;1819declarecontinuehandlerfornotfoundsetexit_flag =1;2021-- 创建临时表收集数据22CREATEtemporaryTABLE`temp_table_emp`(23`empno`INT(11)NOTNULLCOMMENT'员工编号',24`ename`VARCHAR(32)NULLCOMMENT'员工姓名'COLLATE'utf8_general_ci',25`sal`DECIMAL(7,2)NOTNULLDEFAULT'0.00'COMMENT'薪资',26PRIMARYKEY(`empno`)USINGBTREE27)28COLLATE='utf8_general_ci'29ENGINE=InnoDB;  3031    open emp_cursor;3233    c_loop:loop34        fetch emp_cursor into emp_no,emp_name,emp_sal;353637        if exit_flag != 1 then38insertintotemp_table_empvalues(emp_no,emp_name,emp_sal); 39        else40            leave c_loop;41endif;4243endloopc_loop;4445select*fromtemp_table_emp;4647select@sex_res;-- 仅仅是看一下会不会执行到48    close emp_cursor;4950end$$5152call sp_create_table02('RESEARCH');

原文链接:https://www.tuicool.com/articles/INf2qe3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章