MySQL从5.0版已经开始全力支持储存操作形式过程和表达式。储存操作形式过程和表达式能将繁杂的SQL方法论PCB在一同,插件无需高度关注储存操作形式过程和表达式外部繁杂的SQL方法论,而只须要单纯地初始化储存操作形式过程和表达式方可。

1.储存操作形式过程简述
1.1 简述
涵义:储存操作形式过程的英语是 Stored Procedure 。它的价值观很单纯,是几组历经 事先校对 的 SQL 句子的PCB。
继续执行操作形式过程:储存操作形式过程事先储存在 MySQL 伺服器上,须要继续执行的这时候,插件只须要向服务端收到初始化储存操作形式过程的指示,服务端就能把事先储存好的这一连串 SQL 句子全数继续执行。
益处:
精简操作形式,提升了sql句子的宠信性,增加了合作开发程序员的阻力增加操作形式操作形式过程中的误判,降低成本增加互联网统计数据传输量(插件不须要把大部份的 SQL 句子透过互联网发送给伺服器)增加了 SQL 句子曝露在网路上的信用风险,也提升了统计数据查阅的可靠性和快照、表达式的对照:
它和快照有著反之亦然的缺点,明晰、安全可靠,还能增加互联网统计数据传输量。但是它和快照相同,快照是交互式表 ,一般来说不对下层状态参数间接操作形式,而储存操作形式过程是系统化的 SQL,能间接操作形式下层状态参数 ,较之于面向全国子集的操作形式形式,能实现许多更繁杂的信息处理。
一旦储存操作形式过程被创建出来,使用它就像使用表达式一样单纯,我们间接透过初始化储存操作形式过程名方可。相较于表达式,储存操作形式过程没有返回值。
1.2 分类
储存操作形式过程的参数类型能是IN、OUT和INOUT。根据这点分类如下:
没有参数(无参数无返回)仅仅带 IN 类型(有参数无返回)仅仅带 OUT 类型(无参数有返回)既带 IN 又带 OUT(有参数有返回)带 INOUT(有参数有返回)注意:IN、OUT、INOUT 都能在一个储存操作形式过程中带多个。
2.创建储存操作形式过程
2.1 语法结构
创建储存操作形式过程的语法:
CREATEPROCEDURE 储存操作形式过程名(IN|OUT|INOUT 参数名 参数类型,…) [characteristics …]BEGIN 储存操作形式过程体 END这个结构就类似于Java中的方法定义了
修饰符 返回类型 方法名(参数类型 参数名,…)
{
方法体;
}
语法结构说明:
参数前面的符号的意思

形参类型能是 MySQL统计数据库中的任意类型。
characteristics 表示创建储存操作形式过程时指定的对储存操作形式过程的约束条件,其取值信息如下:
LANGUAGE SQL
| [NOT] DETERMINISTIC
|CONTAINS SQLNO SQL | READS SQL DATA | MODIFIES SQL DATA
| SQL SECURITYDEFINERINVOKER
| COMMENT string

储存操作形式过程体中能有多条 SQL 句子,如果仅仅一条SQL 句子,则能省略 BEGIN 和 END编写储存操作形式过程并不是一件单纯的事情,可能储存操作形式过程中须要繁杂的 SQL 句子。
BEGIN…END:BEGIN…END 中间包含了多个句子,每个句子都以(;)号为结束符。DECLARE:DECLARE 用来声明变量,使用的位置在于 BEGIN…END 句子中间,而且须要在其他句子使用之前进 行变量的声明。SET:赋值句子,用于对变量进行赋值。SELECT… INTO:把从状态参数中查阅的结果存放到变量中,也是为变量赋值。须要设置新的结束标记, DELIMITER 新的结束标记
因为MySQL默认的句子结束符号为分号‘;’。为了避免与储存操作形式过程中SQL句子结束符相冲突,须要使用DELIMITER改变储存操作形式过程的结束符。比如:“DELIMITER //”句子的作用是将MySQL的结束符设置为//,并以“END //”结束储存操作形式过程。储存操作形式过程定义完毕之后再使用“DELIMITER ;”恢复默认结束符。DELIMITER也能指定其他符号作为结束符。当使用DELIMITER指示时,应该避免使用反斜杠(‘\’)字符,因为反斜线是MySQL的转义字符。
举例说明:
DELIMITER $ CREATEPROCEDURE 储存操作形式过程名(IN|OUT|INOUT 参数名 参数类型,…) [characteristics …]BEGINsql句子1; sql句子2; END $DELIMITER ;2.2 案例代码
案例1:创建一个储存操作形式过程 select_all_student ,查看 student 中的大部份统计数据
DELIMITER $CREATEPROCEDURE select_all_student()BEGINselect * from student ;END $DELIMITER ;案例2:创建储存操作形式过程 avg_score_grade(),返回成绩表中大部份学员的平均分。
DELIMITER //CREATEPROCEDURE avg_score_grade()BEGINselectavg(grade) from score;END //DELIMITER ;案例3:创建储存操作形式过程 get_max_grade(),查看考试成绩的最高分
DELIMITER ;DELIMITER //CREATEPROCEDUREget_max_grade()LANGUAGESQl# 储存操作形式过程由SQL组成 NOTDETERMINISTIC# 结果不确定CONTAINS SQL# 当前储存操作形式过程子程序包含SQL句子SQLSECURITY DEFINER # 当前创建的用户才能继续执行COMMENT查看最高成绩# 注释BEGINselectmax(grade) from score;END //DELIMITER ;案例4:创建储存操作形式过程get_min_grade(),查看“score”表的最低薪资值。并将最低薪资透过OUT参数“ms”输出
DELIMITER //CREATEPROCEDURE get_min_grade(OUT ms int)BEGINselectmin(grade) into ms from score;END //DELIMITER ;初始化操作形式过程
CALL get_min_grade(@ms);查看变量
SELECT @ms ;案例5:创建储存操作形式过程get_someone_sumgrade(),查看“score”表的某个学员的总成绩,并用IN参数stuid输入员工姓名
DELIMITER //CREATEPROCEDURE get_someone_sumgrade(IN stuid int)BEGINselectsum(grade) from score where stu_id = stuid;END //DELIMITER ;初始化操作形式过程
CALL get_someone_sumgrade(906) ;案例6:创建储存操作形式过程get_someone_sumgrade2(),查看“score”表的某个学员的总成绩,并用IN参数stuid输入员工姓名.并用OUT参数sumgrade来输出总成绩
DELIMITER //CREATEPROCEDURE get_someone_sumgrade2(IN stuid int,OUT sumgrade int)BEGINselectsum(grade)into sumgrade from score where stu_id = stuid;END //DELIMITER ;初始化操作形式过程
CALL get_someone_sumgrade2(901,@sumgrade);查看变量
SELECT @sumgrade ;案例7:创建储存操作形式过程show_student_department(),查阅某个学员的院系,并用INOUT参数“stu_x”输入学员姓名,输出院系名称。
DELIMITER //CREATEPROCEDURE show_student_department(INOUT stu_x VARCHAR(20))BEGINselect department intostu_xfrom student wherename = stu_x;END //DELIMITER ;初始化操作形式过程
SET @stu_x := 波波烤鸭;CALLshow_student_department(@stu_x);查看变量
SELECT @stu_x ;3.初始化储存操作形式过程
3.1 初始化的格式
储存操作形式过程有多种初始化方法。储存操作形式过程必须使用CALL句子初始化,并且储存操作形式过程和统计数据库相关,如果要继续执行其他统计数据库中的储存操作形式过程,需要指定统计数据库名称,例如CALL dbname.procname。
CALL 储存操作形式过程名称(实参列表);针对储存操作形式过程相同类型的参数,初始化的形式也有区别:
1> 初始化in模式的参数
CALL sp1(值) ;2>调用out模式的参数
SET @paras ;CALL sp1(@paras) ;SELECT @paras ;3>初始化INOUT模式的参数
SET @paras = 值;CALL sp1(@paras) ;SELECT @paras ;3.2 代码举例
创建储存操作形式过程,同时实现累加运算,计算 1+2+…+n 等于多少。具体的代码如下:
DELIMITER // CREATEPROCEDURE`add_num`(IN n INT)BEGINDECLARE i INT; DECLAREsumINT; SET i = 1; SETsum = 0; WHILE i <= n DOSETsum = sum + i; SET i = i +1; ENDWHILE; SELECTsum; END // DELIMITER ;如果你用的是 Navicat 工具,那么在编写储存操作形式过程的这时候,Navicat 会自动设置 DELIMITER 为其他符号,我们不须要再进行 DELIMITER 的操作形式。
间接使用 CALL add_num(50); 方可。这儿我传入的参数为 50,也是统计 1+2+…+50 的积累之和。
3.3 如何调试
在 MySQL 中,储存操作形式过程不像普通的编程语言(比如 VC++、Java 等)那样有专门的集成合作开发环境。因此,你能透过 SELECT 句子,把程序继续执行的中间结果查阅出来,来调试一个 SQL 句子的正确性。调试成功之后,把 SELECT 句子后移到下一个 SQL 句子之后,再调试下一个 SQL 句子。这样 逐步推进 ,就能完成对储存操作形式过程中大部份操作形式的调试了。当然,你也能把储存操作形式过程中的 SQL 句子复制出来,逐段单独调试.
4.储存表达式
前面学习了很多表达式,使用这些表达式能对统计数据进行的各种处理操作形式,极大地提升用户对统计数据库的管理效率。MySQL全力支持自定义表达式,定义好之后,初始化形式与初始化MySQL预定义的系统表达式一样。
4.1 语法结构
前面我们学习过了length,concat,substr等表达式。
表达式的语法结构
CREATEFUNCTION 表达式名(参数名 参数类型,…) RETURNS 返回值类型 [characteristics …] BEGIN 表达式体 #表达式体中肯定有 RETURN 句子 END说明:
参数列表:指定参数为IN、OUT或INOUT只对PROCEDURE是合法的,FUNCTION中总是默认为IN参数。RETURNS type 句子表示表达式返回统计数据的类型;RETURNS子句只能对FUNCTION做指定,对表达式而言这是 强制 的。它用来指定表达式的返回类型,而且表达式体必须包含一个 RETURN value 句子。characteristic 创建表达式时指定的对表达式的约束。取值与创建储存操作形式过程时相同,这儿不再赘述。表达式体也能用BEGIN…END来表示SQL代码的已经开始和结束。如果表达式体只有一条句子,也能省略BEGIN…END。4.2 初始化储存操作形式过程
在MySQL中,储存表达式的使用方法与MySQL外部表达式的使用方法是一样的。换言之,用户自己定义的储存表达式与MySQL外部表达式是一个性质的。区别在于,储存表达式是用户自己定义的,而外部表达式是MySQL 的合作开发者定义的。
SELECT 表达式名(参数列表) ;4.3 案例讲解
案例1:创建储存表达式,名称为department_by_name(),参数定义为空,该表达式查阅波波烤鸭的所在院系名称,并返回,统计数据类型为字符串型。
DELIMITER //CREATEFUNCTION department_by_name()RETURNSVARCHAR(25)DETERMINISTICBEGINRETURN (SELECT department from student wherename = 张老二 );END //DELIMITER ;初始化表达式
SELECT department_by_name() ;案例2:创建储存函数,名称为name_by_id(),参数传入stu_id,该表达式查阅student的name,并返回,统计数据类型为字符串型。
DELIMITER //CREATEFUNCTIONname_by_id(stu_idINT)RETURNSVARCHAR(25)DETERMINISTICBEGINRETURN (SELECTnamefrom student whereid = stu_id );END //DELIMITER ;初始化操作形式过程
SET @stu_id=902;SELECT name_by_id(@stu_id);# 或SELECT name_by_id(903);案例3:创建储存表达式count_by_id(),参数传入stuid,该表达式查阅score中对应学员的考试总成绩,并返回,统计数据类型为整型。
DELIMITER //CREATEFUNCTION count_by_id(stuid INT)RETURNSINTDETERMINISTICBEGINRETURN (SELECTsum(grade) from score where stu_id = stuid );END //DELIMITER ;初始化操作形式过程
SET@stuid=902;SELECT count_by_id(@stuid);# 或SELECT count_by_id(903);4.4 对照操作形式过程和表达式

此外,储存表达式能放在查阅句子中使用,储存操作形式过程不行。反之,存储操作形式过程的功能更加强大,包括能继续执行对表的操作形式(比如创建表,删除表等)和事务操作形式,这些功能是储存表达式不具备的。
5.查看、修改、删除操作形式过程和表达式
5.1 查看
创建完之后,怎么知道我们创建的储存操作形式过程、储存表达式是否成功了呢?
MySQL储存了储存操作形式过程和表达式的状态信息,用户能使用SHOW STATUS句子或SHOW CREATE句子来查看,也可间接从系统的information_schema统计数据库中查阅。这儿介绍3种方法。
使用SHOW CREATE句子查看储存操作形式过程和表达式的创建信息
语法结构
SHOWCREATE {PROCEDURE | FUNCTION} 储存操作形式过程名或表达式名使用 SHOW STATUS 句子查看储存操作形式过程和表达式的状态信息
语法结构
SHOW {PROCEDURE | FUNCTION} STATUS [LIKEpattern]这个句子返回子程序的特征,如统计数据库、名字、类型、创建者及创建和修改日期。
从infomation_schema.Routines表中查看储存操作形式过程和表达式的信息
MySQL中储存操作形式过程和表达式的信息储存在information_schema统计数据库下的Routines表中。能透过查阅该表的记录来查询储存操作形式过程和表达式的信息。其基本语法形式如下:
SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME=储存操作形式过程或表达式的名 [ANDROUTINE_TYPE = {PROCEDURE|FUNCTION}];说明:如果在MySQL统计数据库中存在储存操作形式过程和表达式名称相同的情况,最好指定ROUTINE_TYPE查阅条件来
指明查阅的是储存操作形式过程还是表达式。
5.2 修改
修改储存操作形式过程或表达式,不影响储存操作形式过程或表达式功能,只是修改相关特性。使用ALTER句子同时实现。
ALTER {PROCEDURE | FUNCTION} 储存操作形式过程或表达式的名 [characteristic …]其中,characteristic指定储存操作形式过程或表达式的特性,其取值信息与创建储存操作形式过程、表达式时的取值信息略有相同
CONTAINS SQLNO SQL | READS SQL DATA | MODIFIES SQL DATA
| SQL SECURITYDEFINERINVOKER
| COMMENT string
CONTAINS SQL ,表示子程序包含SQL句子,但不包含读或写统计数据的句子。
NO SQL ,表示子程序中不包含SQL句子。
READS SQL DATA ,表示子程序中包含读统计数据的句子。
MODIFIES SQL DATA ,表示子程序中包含写统计数据的句子。
SQL SECURITYDEFINERINVOKER,指明谁有权限来继续执行。DEFINER ,表示只有定义者自己才能继续执行。INVOKER ,表示初始化者能继续执行。
COMMENT string ,表示注释信息。
修改储存操作形式过程使用ALTER PROCEDURE句子,修改储存表达式使用ALTER FUNCTION句子。但是,这两个句子的结构是一样的,句子中的大部份参数也是一样的。
案例1:修改储存操作形式过程 show_student_department 的定义。将读写权限改为MODIFIES SQL DATA,并指明初始化者能继续执行,代码如下:
ALTERPROCEDURE show_student_departmentMODIFIES SQLDATASQLSECURITY INVOKER ;查阅修改后的信息:
SELECT specific_name,sql_data_access,security_type FROMinformation_schema.`ROUTINES`WHERE routine_name = show_student_departmentAND routine_type = PROCEDURE;结果显示,储存操作形式过程修改成功。从查阅的结果能看出,访问统计数据的权限(SQL_DATA_ ACCESS)已经变成MODIFIES SQL DATA,安全可靠类型(SECURITY_TYPE)已经变成INVOKER。
案例2:修改储存表达式count_by_id的定义。将读写权限改为READS SQL DATA,并加上注释信息“FIND NAME”,代码如下:
ALTERFUNCTION count_by_id READSSQLDATACOMMENTFIND NAME ;5.3 删除
删除储存操作形式过程和表达式,能使用DROP句子,其语法结构如下:
DROP {PROCEDURE | FUNCTION} [IFEXISTS] 储存操作形式过程或表达式的名IF EXISTS:如果程序或表达式不储存,它能防止发生错误,产生一个用SHOW WARNINGS查看的警告。
举例:
DROPPROCEDURE CountProc;DROPFUNCTION CountProc;6.储存操作形式过程的争议
尽管储存操作形式过程有诸多缺点,但是对于储存操作形式过程的使用,一直都存在着很多争议,比如有些公司对于大型项目要求使用储存操作形式过程,而有些公司在手册中明确禁止使用储存操作形式过程,为什么这些公司对储存操作形式过程的使用需求差别这么大呢?
6.1 缺点
储存操作形式过程能一次校对多次使用。储存操作形式过程只在创建时进行校对,之后的使用都不须要重新校对,这就提升了 SQL 的继续执行效率。
能增加合作开发工作量。将代码 PCB 成模块,实际上是编程的核心价值观之一,这样能把繁杂的问题拆解成相同的模块,然后模块之间能 重复使用 ,在增加合作开发工作量的同时,还能保证代码的结构明晰。
储存操作形式过程的可靠性强。我们在设定储存操作形式过程的这时候能 设置对用户的使用权限 ,这样就和快照一样具有较强的可靠性。
能增加互联网统计数据传输量。因为代码PCB到储存操作形式过程中,每次使用只须要初始化储存操作形式过程方可,这样就增加了互联网统计数据传输量。
良好的PCB性。在进行相对繁杂的统计数据库操作形式时,原本须要使用一条一条的 SQL 句子,可能要连接多次统计数据库才能完成的操作形式,现在变成了一次储存操作形式过程,只须要 连接一次方可 。
6.2 缺点
基于上面这些缺点,不少大公司都要求大型项目使用储存操作形式过程,比如微软、IBM 等公司。但是国内的阿里并不推荐合作开发人员使用储存操作形式过程,这是为什么呢?
阿里合作开发规范
【强制】禁止使用储存操作形式过程,储存操作形式过程难以调试和扩展,更没有移植性。
储存操作形式过程虽然有诸如上面的益处,但缺点也是很明显的。
可移植性差。储存操作形式过程不能跨统计数据库移植,比如在 MySQL、Oracle 和 SQL Server 里编写的储存操作形式过程,在换成其他统计数据库时都须要重新编写。
调试困难。只有少数 DBMS 全力支持储存操作形式过程的调试。对于繁杂的储存操作形式过程来说,合作开发和维护都不容易。虽然也有许多第三方工具能对储存操作形式过程进行调试,但要收费。
储存操作形式过程的版管理很困难。比如状态参数索引发生变化了,可能会导致储存操作形式过程失效。我们在合作开发软件的这时候往往须要进行版管理,但是储存操作形式过程本身没有版控制,版迭代更新的这时候很麻烦。
它不适合高并发的场景。高并发的场景须要增加统计数据库的阻力,有时统计数据库会采用分库分表的形式,而且对可扩展性要求很高,在这种情况下,储存操作形式过程会变得难以维护, 增加数据库的阻力 ,显然就不适用了。
小结:
储存操作形式过程既方便,又有局限性。尽管相同的公司对储存操作形式过程的态度不一,但是对于我们合作开发人员来说,不论怎样,掌控储存操作形式过程都是必备的技能之一。