人生不如意事,十之八九,可与人言,不过一二。每个人都会经历一段暗淡无光的日子。
或是无人问津的低谷,亦是生活的窘迫,工作的失意,家庭琐碎,爱得惶惶,不可终日。
越是这些艰难的时刻,越希望你能坚持。因为难走的都是上坡路。
杨绛先生曾说:一个人经过不同程度的锻炼,就获得不同程度的修养,不同程度的效益,
好比香料,捣的越碎,磨得越细,香得越浓烈,过日子没有什么一帆风顺,但也没有
什么穷途末路,纵使眼前是一片苦海。我们也要学会,做自己的摆渡人,生活就像一盒巧克力,
你永远不知道你下一刻拿到的是什么,但是依旧要保持对未知的期待,没有过不去的坎坷,只有过不去的人。
一片乌云,挡不住所有的阳光,一次跌宕,泥泞不了整个人生,每个人都会遇见痛苦迷茫,
但这都是生命赐给我们的礼物。
是它教会了我们,如何与世界和平相处,如何与苦痛,握手言和,不论现实多么残酷,
都要相信,总有希望在转角,生活不会亏待一个不断进取的人。黑夜再长,天总会亮。
但愿所有的负担,都变成礼物,所受的苦,都能照亮未来的路,那些你吃过的苦,都成为明天的糖。
—————— 一禅心灵庙语
MySQL5.0
版本开始支持存储过程和函数。存储过程和函数能够将复杂的SQL语句逻辑封装在一起,应用程序无需关注存储过程和函数内部复杂的SQL逻辑,而只需要简单地调用存储过程和函数即可。
将多个 SQL
语句组合成一个只需要使用命令 CALL xxx
就能执行的集合就称为存储过程(Stored Procedure)
“存储Stored”表示保存,“过程Procedure” 表示步骤。它的思想很简单,就是一组经过预先编译的 SQL
语句的封装。执行过程:存储过程预先存储在 MySQL 服务器上,需要执行的时候,客户端只需要向服务器端发出调用存储过程的命令,服务器端就可以把预先存储好的这一些列 SQL
语句全部执行。也就是说:存储过程是将一系列SQL语句步骤归纳并存储起来的集合。一般存储过程主要用于对数据表的增删改,而不是简单的查询,查询主要是存储函数
由于可以自动执行许多事先准备好的命令,所以处理效率很高。但是,在存储了重要数据的数据库中,执行没有经过充分验证的存储过程是非常危险的,这一点需要我们牢记。
存储过程的好处
存储过程的参数类型可以是 IN,OUT 和 INOUT
。根据这点分类如下:
IN
类型 (有参数无返回值)OUT
类型 (无参数有返回值)IN
又带 OUT
(有参数有返回值)INOUT
(有参数有返回值)注意:IN,OUT,INOUT
都可以在一个存储过程中带多个 。
当创建存储过程时,我们需要像下面这样执行CREATE PROCEDURE
命令
格式如下:
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...] -- 注意在MySQL中参数的数据类型,是在参数的后面
DELIMITER $-- 修改;分号为 xxx
BEGIN -- 开始
存储过程体
sql 语句1
sql 语句2
END $ -- 结束
DELIMITER ; -- 修改回去
BEGIN ... END: BEGIN 存储过程的开始位置 END: 是存储过程的结束位置,
中间包含了多个sql语句,每个语句都以; 分号为结束符
SELECT ... INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值
DELIMITER xxx 将;分号改为一个新的结束标记
DELIMITER ; 将;分号修改回去,防止影响后面的正常使用
参数前面的符号的意思
IN
: 当前参数为 输入参数 ,也就是表示入参; 存储过程只是读取这个参数的值。如果没有定义参数种类,默认就是 IN
,表示输入参数;简单的说当你需要对存储过程,输入参数时,需要将该参数定义为 IN
OUT
:当前参数为输出参数 ,也就是出参 ;执行完成之后,调用这个存储过程的客户端或者应用程序就可以读取这个参数返回的值了。简单的说当你需要对存储过程,输出参数时,需要将该参数定义为 OUT
INOUT
: 当参数既是作为输入参数,也是作为输出参数 。使用形参类型可以是 MySQL数据库额任意类型,和别的 Java ,C语言不同,MySQL中的数据类型在存储过程中是写在参数名的后面的。关于MySQL数据类型的详细讲解,大家可以移步到 (Mysql 详解所有的数据类型_ChinaRainbowSea的博客-CSDN博客 。
characteristics
表示存储过程时指定的对存储过程的约束条件,其取值信息如下:
LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'
LANGUAGE SQL
: 说明存储过程执行是由SQL语句组成的,当前系统支持的语言为 SQL
[NOT] DETERMINSTIC
: 指明存储过程执行的结果是否确定。DETERMINISTIC
表示结果是确定的。每次执行存储过程时,相同的输入会得到相同的输出。NOT DETERMINISTIC
表示结果是不确定的,相同的输入可能得到不同的输出。如果没有指定任意一个值,默认为NOT DETERMINSTIC
。
{ CONTANS SQL | NOT SQL | READS SQL DATA | MODIFIES SQL DATA }
:指明子程序使用 SQL 语句的限制。
CONTAINS SQL
表示当前存储过程的子程序包含 SQL 语句,但是并不包含读写数据的SQL语句NO SQL
表示当前存储过程的子程序中不包含任何 SQL 语句READS SQL DATA
表示当前存储过程的子程序中包含读数据的SQL语句MODIFIES SQL DATA
表示当前存储过程的子程序中包含写数据的SQL语句。CONTAINS SQL
SQL SECURITR{DEFINER | INVOKER }
:执行当前存储过程的权限,即指明那些用户能够执行当前存储过程。
DEFINER
表示只有当前存储过程的创建者或者定义者才能执行当前存储过程INVOKER
表示拥有当前存储过程的访问权限的用户能够执行当前存储过程COMMENT 'xxxx'
注释信息:可以用来描述存储过程。**存储过程体中可以有多条 SQL 语句,如果仅仅一条 SQL 语句,则可以省略 BEGIN
和 END
**
编写存储过程并不是一件简单的事情,可能存储过程中需要复杂的 SQL
语句。如下:
1.BEGIN ... END 中间包含了多个SQL语句,每个语句都以 (;)分号结束。
BEGIN 表示开始, END 表示结束
2.DECLARE: 用来声明变量,使用的位置在于 BEGIN...END 语句中间,而且需要在其他语句使用之前进行变量的声明
3.SET: 赋值语句,用于对变量进行赋值。
4.SELECT ... INTO 把数据表中查询的结果存放到变量中,也就是为变量赋值.
需要设定新的结束标志
DELIMITER $ 将分号;结束标志设置成 $
...
DELIMITER ; 将分号修改回去,防止影响后面的正常使用
为什么要修改结束符号
因为MySQL 默认的语句结束符号为分号 “;”。为了避免与存储过程中 SQL 语句的结束符的(;)分号冲突了,
需要使用
DELIMITER
改变存储过程的结束符。比如:DELIMITER $
语句的作用是将MySQL的结束符设置为$
并以
END $
结束存储过程。存储过程定义完毕之后再使用DELIMITER ;
恢复到默认结束符。
DELIMITER
也可以指定其他符号作为结束符。当使用DELIMITER
命令时,应该避免使用反斜杆'\'
字符,因为反斜杠是MySQL的转义字符。
示例
DELIMITER $ -- 将结束符;修改为$
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...]
BEGIN -- 开始
sql语句1;
sql语句2;
END $ -- 结束
DELIMITER ; -- 修改回去
存储过程有多种调用方法。存储过程必须使用 CALL
语句调用,并且存储过程和数据库相关,如果要执行其他数据库中的存储过程,需要指定数据库名称,例如CALL.数据库名.存储过程
格式如下:
CALL 存储过程名; -- 调用当前数据库下的存储过程
CALL 数据库名.存储过程名; -- 调用指定数据库下的存储过程
创建一个无参数,无返回值的存储过程,就是参数列表中写任何参数
格式如下:
DELIMITER $ -- 修改;分号结束符
CREATE PROCEDURE 存储过程名()
BEGIN -- 开始
sql语句1;
sql语句2;
END $ -- 使用修改后的结束符,表示结束
DELIMITER ; -- 修改回去,防止影响后面的使用
举例: 创建存储过程select_all_data(),查看 emps 表的所有数据
DELIMITER $ -- 修改;分号结束符为$
CREATE PROCEDURE select_all_data() -- 无参数无返回
BEGIN -- 开始
SELECT *
FROM emps;
END $ -- 使用修改后的结束符$,表示结束
DELIMITER ; -- 修改回去,用于后面的正常使用
无参无返回的存储过程的调用,格式如下:
CALL 存储过程名;
举例: 调用刚刚创建的存储过程select_all_data()
CALL select_all_data;
IN
类型:创建存储过程IN
: 当前参数为 输入参数 ,也就是表示入参; 存储过程只是读取这个参数的值。如果没有定义参数种类,默认就是 IN
,表示输入参数;简单的说当你需要对存储过程,输入参数时,需要将该参数定义为 IN
创建一个仅仅带 IN
类型的存储过程,就是参数列表中写添加上IN 类型 以及变量名 变量名的数据类型 。当然MySQL中默认没有指明的类型参数变量,默认是 IN
,所以可以省略,但是不建议。
格式如下:
注意的是,MySQL中参数的数据类型是写在变量的后面的
DELIMITER $ -- 修改;分号结束符
CREATE PROCEDURE 存储过程名(IN 变量名 该变量的数据类型)
BEGIN -- 开始
sql语句1;
sql语句2;
END $ -- 使用修改后的结束符,表示结束
in
类型:调用存储过程调用带有in
类型的存储过程,调用该存储过程时,需要输入与之对应的数据类型的变量
方式1:直接调用并插入内容
CALL 存储过程名(输入的内容);
方式2:先定义好变量,再输入变量
set 定义变量,MySQL中的变量的最前面需要加@
SET @变量名 = xxx; -- 定义变量,并赋值
CALL 存储过程名(@变量名);
调用上述刚刚创建的avg_employee_salary(),将 employee_id = 132 的员工的,salary 增加 5000
CALL avg_employee_salary(132);
OUT
类型:创建存储过程OUT
:当前参数为输出参数 ,也就是出参 ;执行完成之后,调用这个存储过程的客户端或者应用程序就可以读取这个参数返回的值了。简单的说当你需要对存储过程,输出参数时,需要将该参数定义为 OUT
仅仅带有OUT
类型:创建存储过程,我们需要在创建存储过程的参数列表中添加上( out 变量名 数据类型)
同时我们需要使用 INTO
将查询到的结果返回赋值到 out 变量中,如果存在返回值,则需要多个 out 变量,每个返回的值,需要和 out 变量一一有序的对应上,不然可能会返回到错误的 out 变量上
注意:尽量让返回的 out 的参数变量的数据类型 >=
对应表中的数据类型,防止返回赋值的数据的丢失
格式如下:
DELIMITER $ -- 将分号;结束符,修改为$
CREATE PROCEDURE 存储过程名(OUT 变量名1 该变量名1的数据类型,OUT 变量名2, 该变量名2的数据类型)
BEGIN -- 开始
sql语句1 INTO 变量名1, 变量名2; -- into将查询结果返回到 变量名1 变量名2中
END $ -- 使用修改后的结束符$,表结束
DELIMITER ; -- 修改回去
举例: 创建存储过程show_min_salary(),查看“emps”表的最低薪资值。并将最低薪资通过OUT参数“ms”输出
DELIMITER $ -- 将分号;结束符修改为 $
CREATE PROCEDURE show_min_salary(OUT ms DOUBLE(8,2))
BEGIN -- 开始
SELECT MIN(salary) INTO ms -- into将查询的结果返回赋值到 ms 参数变量上
FROM emps;
END $ -- 使用修改后的结束符$,表示结束
DELIMITER ; -- 修改回去,防止影响后面的正常使用
out
类型:调用存储过程调用 out
模式的存储过程,需要使用 set
定义变量,用于存储过程中返回的值。返回后再使用SELECT
查看该返回的变量值。
注意:MySQL中定义的变量名的最前面是@
格式如下:
CALL 存储过程名(@变量名1);
SELECT @变量名1 -- 查看@变量名1 的值
FROM DUAL; -- dual 伪表
举例: 查看上述创建的 show_min_salary() 存储过程中,查询到的返回值
SET @ms = 0;
CALL show_min_salary(@ms);
SELECT @ms
FROM DUAL;
IN
又带 OUT
类型:创建存储过程IN
: 当前参数为 输入参数 ,也就是表示入参; 存储过程只是读取这个参数的值。如果没有定义参数种类,默认就是 IN
,表示输入参数;简单的说当你需要对存储过程,输入参数时,需要将该参数定义为 IN
OUT
:当前参数为输出参数 ,也就是出参 ;执行完成之后,调用这个存储过程的客户端或者应用程序就可以既带有 IN
又带 OUT
类型:创建存储过程:就是既有输入,又有输出呗。
创建该类型的存储过程时:参数列表上分别添加上 IN
输入的参数变量和 OUT
输出的参数变量
注意:输入的 IN
的参数变量的数据类型尽量和对应表中的数据类型一致,防止比较判断错误,同样输出的 OUT
的参数变量的数据类型也尽量 >= 对应表中的数据类型,防止太小了导致存储的信息丢失了
格式如下:
DELIMITER $ -- 将分号;结束符,修改为$
CREATE PROCEDURE 存储过程名(IN 变量名1 该变量名1的数据类型,OUT 变量名2, 该变量名2的数据类型)
BEGIN -- 开始
sql语句1 INTO 变量名2; -- into将查询结果返回到 变量名1 变量名2中
END $ -- 使用修改后的结束符$,表结束
DELIMITER ; -- 修改回去
举例: 创建存储过程show_someone_salary2(),查看“emps”表的某个员工的薪资,并用IN参数empname,输入员工姓名,用OUT参数empsalary输出员工薪资。
DELIMITER // -- 将分号;结束符,修改为//
CREATE PROCEDURE show_someone_salary2(IN empname VARCHAR(25), OUT empsalary DOUBLE(8,2))
BEGIN -- 开始
SELECT salary INTO empsalary -- 将查询的结果,返回赋值到 OUT empsalary 当中
FROM emps
WHERE last_name = empname;
END // -- 使用修改后的结束符,表示结束
DELIMITER; -- 修改回去,防止影响后面的正常使用
IN
又带 OUT
类型:调用存储过程调用既带有 IN
又带 OUT
类型:调用存储过程,我们需要定义变量用于 out 返回的数据的存储
注意:MySQL中定义的变量名的最前面是@
方式1: 只定义 OUT 返回类型的变量
SET @变量名 = 0;
CALL 存储过程名(IN输入参数值,@变量名);
SELECT @变量名 -- 查看返回的存储的数据
FROM DUAL; -- dual 伪表;
方式2:定义全部的参数变量,
SET @变量名1 = 0,@变量名2 = 0; -- @变量名1 是IN的参数,@变量名2 是OUT的参数
CALL 存储过程名(@变量名1,@变量名2);
SELECT @变量名2 -- 查看返回的存储的数据
FROM DUAL; -- dual 伪表;
举例: 调用刚刚创建的 show_someone_salary2() 存储过程,查看 emps 表中 姓名为 Abel 的工资
SET @empname = 'Abel',@empsalary = 0;
CALL show_someone_salary2(@empname,@empsalary);
SELECT @empsalary -- 查看返回的保存的数据
FROM DUAL; -- dual 伪表
INOUT
类型 :创建存储过程INOUT
: 当参数既是作为输入参数,也是作为输出参数 。使用当我们创建存储过程中定义了 INOUT
参数的变量,输入参数变量是它,输出参数变量也是它
定义的 inout 参数变量,尽量和对应的表中的数据类型一一对应,防止比较判断错误,以及存储返回数据信息截断错误。
格式如下:
DELIMITER $ -- 将分号;修改为$ 作为结束符
CREATE PROCEDURE 存储过程名(INOUT 变量名 数据类型)
BEGIN -- 开始位置
sql语句1 INTO 变量名 -- 将查询到的结果返回赋值到 inout 变量中
END $ -- 使用修改后的$作为结束符
DELIMITER; -- 修改回去,防止影响后面的正常使用
举例: 创建存储过程show_mgr_name(),查询 emps 表中 某个员工领导的姓名,并用INOUT参数“empname”输入员 工姓名,输出领导的姓名。
DELIMITER $ -- 将分号;修改为$ 作为结束符
CREATE PROCEDURE show_mgr_name(INOUT empname VARCHAR(25))
BEGIN -- 开始
SELECT last_name INTO empname -- 将查询结果返回赋值到 empname 当中
FROM emps
WHERE employee_id = (
SELECT manager_id
FROM emps
WHERE last_name = empname);
END$ -- 使用修改后的$,表示结束
DELIMITER ; -- 修改回去,防止影响后面的正常使用
INOUT
类型 :调用存储过程仅仅带有INOUT
类型的存储过程的调用
首先定义变量,用于存储过程的返回数值 。即可,格式如下:
SET @变量名 = '输入的值';
CALL 存储过程名(@变量名);
SELECT @变量名 -- 查看返回存储的变量的数据
FROM DUAL ; -- dual 伪表
举例: 将上面刚刚创建的 show_mgr_name() 存储过程,输入 Abel ,查看其领导的姓名
SET @empname = 'Abel';
CALL show_mgr_name(@empname);
SELECT @empname
FROM DUAL;
在MySQL中,存储过程不像普通的编程语言(比如 VC ++,Java 等),那样有专门的集成开发环境。因此,你可以通过 SELECT 语句,把程序执行的中间结果查询出来,来调试一个SQL 语句的正确性。调试成功之后,把 SELECT 语句后移到下一个 SQL语句之后,再调试下一个SQL语句。这样逐步推进,就可以完成对存储过程中所有操作的调试了。当然,你也可以把存储过程中的 SQL 语句复制出来,逐段单独调试。
前面学习了很多函数,使用这些函数可以对数据进行的各种处理操作,极大地提高用户对数据库的管理效率。MySQL支持自定义函数,定义好之后,调用方式与调用MySQL预定义的系统函数一样。
创建完之后,怎么知道我们创建的存储过程、存储函数是否成功了呢?
MySQL存储了存储过程和函数的状态信息,用户可以使用SHOW STATUS
语句或SHOW CREATE
语句来查 看,也可直接从系统的information_schema
数据库中查询。这里介绍3种方法。
方式1:使用SHOW CREATE
语句查看存储过程创建信息
格式如下:
SHOW CREATE PROCEDURE 存储过程名;
可以在DOS 命令中最后面添加上\G分行显示,看上去更加美观
举例: 查看存储过程avg_employee_salary()的信息
SHOW CREATE PROCEDURE avg_employee_salary;
SHOW CREATE PROCEDURE avg_employee_salary\G;
方式2: 使用 SHOW STATUS
语句查看存储过程和函数的状态信息
这个语句返回子程序的特征,如数据库、名字、类型、创建者及创建和修改日期。在不同的数据库中的存储过程也会被检查出来。
格式如下:
SHOW PROCEDURE STATUS LIKE '存储过程名' -- 可以使用正则表达式,模糊查询
可以使用 \G 在 DOS 命令中分行显示更加美观。
举例: 查看存储过程avg_employee_salary()的信息
SHOW PROCEDURE STATUS LIKE 'avg_employee_salary';
方式3: 从information_schema.Routines
表中查看存储过程和函数的信息
MySQL中存储过程和函数的信息存储在 information_schema
数据库下的 Routines
表中。可以通过查询该表的记录来查询存储过程和函数的信息。基本语法形式如下:
SELECT *
FROM information_schema.`ROUTINES`
WHERE routine_name = '存储过程名' AND routine_type = 'PROCEDURE';
-- 指明是存储过程
同样可以在DOS 命令中 的最后面添加上 /G,用于分行显示。
SELECT *
FROM information_schema.`ROUTINES`
WHERE routine_name = '存储过程名' AND routine_type = 'PROCEDURE'\G;
-- 指明是存储过程
说明:如果在MySQL数据库中存在存储过程和函数名称相同的情况,最好指定ROUTINE_TYPE
查询条件来指明查询的是存储过程还是函数。
举例: 查看存储过程avg_employee_salary()的信息
SELECT *
FROM information_schema.`ROUTINES`
WHERE routine_name = 'avg_employee_salary' AND routine_type = 'PROCEDURE';
修改存储过程或函数,不影响存储过程或函数功能,只是修改相关特性。使用ALTER
语句实现。
格式如下:
ALTER PROCEDURE 存储过程名 [characteristic ...]
其中,characteristic 指定存储过程或函数的特征,其取值信息与创建存储过程,函数时的取值信息略有不同。
{ CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'
CONTAINS SQL
表示子程序包含SQL语句,但不包含读或写数据的语句。NO SQL
表示子程序中不包含SQL语句。READS SQL DATA
, 表示子程序中包含读数据的语句。MODIFIES SQL DATA
表示子程序中包含写数据的语句SQL SECURITY {DEFINER | INVOKER}
指明谁有权限来执行DEFINER
表示只有定义者自己才能够执行。INVOKER
表示调用者可以执行COMMENT 'string'
表示注释信息。修改存储过程使用ALTER PROCEDURE
语句,修改存储函数使用ALTER FUNCTION
语句,但是,这两个语句的结构是一样的,语句中的所有参数也是一样的。
举例: 修改存储过程(show_min_salary) 中的CountProc的定义。将读写权限改为MODIFIES SQL DATA,并指明调用者可以执行,添加上柱注释:最低工资,代码如 下:
ALTER PROCEDURE show_min_salary
MODIFIES SQL DATA
SQL SECURITY INVOKER
COMMENT '查询最低工资';
使用SHOW PROCEDURE STATUS LIKE 'show_min_salary';
查询看看
SHOW PROCEDURE STATUS LIKE 'show_min_salary'; -- 查询看看
删除存储过程,可以使用DROP
语句,其语法结构如下:
该方式,当存储过程存在,则删除,如果存储过程不存在,则不删除,同时会报错。
DROP PROCEDURE 存储过程名;
我们可以添加上IF EXISTS
:如果程序或函数不存储,它可以防止发生错误,产生一个用SHOW WARNINGS查看的警告。如果该存储过程存在,则删除,如果不存在,则不删除,不会报错,但会给予警告
格式如下:
DROP PROCEDURE IF EXISTS 存储过程名;
举例:删除存储过程 show_min_salary 。
SHOW CREATE PROCEDURE show_min_salary;
删除
DROP PROCEDURE IF EXISTS show_min_salary;
SHOW CREATE PROCEDURE show_min_salary;
尽管存储过程有诸多优点,但是对于存储过程的使用,一直都存在着很多争议,比如,有些公司对于大型项目要求使用存储过程,而有些公司在手册中明确禁止使用存储过程,为什么这些公司对存储过程的使用需求差别这么答呢 ?
和视图,存储函数的对比:
它和视图,函数的对比:它和视图有着同样的优点,清晰,安全,还可以减少网络传输量。不过它和视图不同,视图是虚拟表,通常不对底层数据表直接操作,而存储过程是程序化的 SQL,可以直接操作,底层数据表,相比于面向集合的操作方式,能够实现一些更复杂的数据处理。一旦存储过程被创建出来。使用它就像使用函数一样简单,我们直接通过调用存储过程名即可。相比较于函数,存储过程是没有返回值的。
存储过程可以一次编译多次使用 ,存储过程只在创建时进行编译,之后的使用都不需要重新编译,这就提升了SQL的执行效率。
可以减少开发工作量 。将代码封装成模块,实际上编程的核心思想之一,这样可以把复杂的问题,拆解成不同模块,然后模块之间可以重复使用,在减少开发工作量的同时,还能保证代码的结构清晰。
存储过程的安全性强 ,我们在设定存储过程的时候可以设置对用户的使用权限,这也只能怪
可以减少网络传输量 ,因为代码封装到存储过程中,每次使用只需要调用存储过程即可,这样就减少了网络传输量
良好的封装性 。在进行相对复杂的数据库操作时,原本需要使用一条一条的 SQL 语句,可能要连接多次数据库才能完成的操作,现在变成了一次存储过程,只需要连接一次即可。
基于上面这些优点,不少大公司都要求大型项目使用存储过程,比如微软,IBM等公司。但是国内的阿里并不推荐开发人员使用存储过程,这是为什么呢?
阿里开发规范 :禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
存储过程虽然有诸如上面的好处,但缺点也是很明显的。
存储过程既方便,又有局限性。 尽管不同的公司对存储过程的态度不一,但是对于我们开发人员来说,不论怎样,掌握存储过程都是必备的技能之一。
前面学习了很多有关于 MySQL中集成的函数, Mysql 中 “必知” 的单行处理函数_ChinaRainbowSea的博客-CSDN博客以及 Mysql —— 多行/聚合/分组函数_ChinaRainbowSea的博客-CSDN博客_mysql 行聚合使用这些函数可以对数据进行的各种处理操作,极大地提高用户对数据库的管理效率。MySQL支持自定义函数,定义好之后,调用方式与调用MySQL预定义的系统函数一样。
存储函数一般主要用于对数据表的数据的查询
创建存储函数与创建存储过程语法上是类似的,都是使用CREATE
创建的
函数中必须要有返回值,而且只能返回一个值
格式如下:
DELIMITER $ -- 修改;分号的结束符为 $
CREATE FUNCTION 函数名(参数名 数据类型)
RETURNS 返回值的数据类型,-- 最好 >= 查询返回的表的数据类型,防止返回的数值存在丢失信息
[characteristics ...]
BEGIN -- 开始
END $ -- 使用修改后的结束符$,表示结束
RETURN (查询结果的返回); -- 注意只能返回一个值
DELIMITER ; -- 修改回去,防止影响后面的正常使用
1.BEGIN ... END 中间包含了多个SQL语句,每个语句都以 (;)分号结束。
BEGIN 表示开始, END 表示结束
2.DECLARE: 用来声明变量,使用的位置在于 BEGIN...END 语句中间,而且需要在其他语句使用之前进行变量的声明
3.SET: 赋值语句,用于对变量进行赋值。
4.SELECT ... INTO 把数据表中查询的结果存放到变量中,也就是为变量赋值.
DELIMITER $ 将分号;结束标志设置成 $
...
DELIMITER ; 将分号修改回去,防止影响后面的正常使用
为什么要修改结束符号
因为MySQL 默认的语句结束符号为分号 “;”。为了避免与存储函数中 SQL 语句的结束符的(;)分号冲突了,
需要使用
DELIMITER
改变存储函数的结束符。比如:DELIMITER $
语句的作用是将MySQL的结束符设置为$
并以
END $
结束存储函数。存储函数定义完毕之后再使用DELIMITER ;
恢复到默认结束符。
DELIMITER
也可以指定其他符号作为结束符。当使用DELIMITER
命令时,应该避免使用反斜杆'\'
字符,因为反斜杠是MySQL的转义字符。
1、参数列表:注意: 指定参数为IN、OUT或INOUT
只对 PROCEDURE
是合法的,对 FUNCTION函数是无效的,因为FUNCTION
中总是默认为IN参 数。
IN
: 当前参数为 输入参数 ,也就是表示入参; 存储过程只是读取这个参数的值。如果没有定义参数种类,默认就是 IN
,表示输入参数;简单的说当你需要对存储函数,输入参数时,需要将该参数定义为 IN
,函数FUNCTION
中总是默认为IN
参 数
RETURNS type
语句表示函数返回的类型;RETURNS
子句只能对 FUNCTION
做指定,对函数而言这是强制的。它用来指定函数的放回类型,而且函数体必须包含一个 RETURN value
语句。
characteristic
创建函数时指定的对函数的约束。取值与创建过程时相同,这里不再累述。BEGIN...END
来表示SQL代码的开始和结束。如果函数体只有一条语句,也可以省略BEGIN...END
。但是,不建议这样。注意:重点来了,创建存储函数时的报错问题。
若在创建存储函数中报错“ you might want to use the less safe log_bin_trust_function_creators variable ”
,有两种处理方法:
我们在创建存储函数,在使用之前需要修改一处设置。如果我们没有指定在执行创建存储函数是就会报错
存储函数有可能对复制视图,和数据的恢复产生影响。因此,参数 log_bin_trust_function_creators
的初始值被设置为 0 ,这样就不能使用存储函数了。(在开启二进制日志功能的情况下log_bin_trust_function_creators
参数用于控制是否可以信任存储函数创建者,防止创建写入二进制日志银日不安全的存储函数。默认值为 0(OFF) 表示用户不能创建或修改存储函数,除非强制使用DETERMINISTIC,READS,SQL DATA 或 NO SQL
特性来声明函数,明确告知 Mysql 服务器这个函数不会修改数据)
。要想使用存储函数,解决方法有两种:
第一种
执行下面的操作修改此设置。
SET GLOBAL log_bin_trust_function_creators = 1;
确认设置是否已正确修改,执行下面的命令
SHOW VARIABLES LIKE 'log_bin_trust_function_creators';
如果log_bin_trust_function_creators
被设置成了 ON
,就可以使用存储函数了。另外,如果重新启动 MySQL,log_bin_trust_function_creators
的值将变回 OFF
。如果想使用存储函数,就需要再执行上述操作了。另外,使用存储函数还需要相应的权限。大家使用的 root 用户中已经包含了这个权限 (Super权限) ,但是普通用户就需要额外添加权限了。
第二种方式
在创建存储函数时加上必要的函数特性:[NOT] DETERMINISTIC”和“{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA}
格式如下:
DELIMITER $ -- 修改;分号的结束符为 $
CREATE FUNCTION 函数名(参数名 数据类型)
RETURNS 返回值的数据类型
-- 如添加如下特征
DETERMINISTIC
CONTAINS SQL
READS SQL DATA
BEGIN -- 开始
END $ -- 使用修改后的结束符$,表示结束
RETURN (查询结果的返回); -- 注意只能返回一个值
DELIMITER ; -- 修改回去,防止影响后面的正常使用
在MySQL中,存储函数的使用与 MySQL内部函数的使用方法是一样的。换言之,用户自己定义的存储函数与MySQL内部函数是一个性质。区别在于,存储函数是用户自己定义的,而内部函数是MySQL的开发者定义的
调用存储函数,格式如下:
SELECT 函数名(实参列表);
举例: 创建存储函数,名称为email_by_name(),参数定义为空,该函数查询emps 表中名字为 Abel 的email,并返回,数据类型为 字符串型。
首先我们先执行修改设置,如下代码:
SET GLOBAL log_bin_trust_function_creators = 1;
再查看是否修改为 on 成功
SHOW VARIABLES LIKE 'log_bin_trust_function_creators';
再创建有关的的存储函数,就不会报错了
DELIMITER $ -- 修改;分号为 $,防止与后面的函数sql语句;分号冲突
CREATE FUNCTION email_by_name()
RETURNS VARCHAR(25) -- 注意多了个 s,返回的数据类型,最好和查询表中的数据类型一致,防止数据截断不全
BEGIN -- 开始
RETURN ( -- 查询结果的返回
SELECT email
FROM emps
WHERE last_name = 'Abel');
END $ -- 使用修改后的$结束符,表示结束
DELIMITER ; -- 修改回去,防止影响后面的正常使用
调用该 email_by_name() 存储函数
SELECT email_by_name();
举例 创建存储函数,名称为emps_dept_avg(),参数传入部门编号 dept_id名称,该函数查询 emps 表中部门编号为 dept_id 的 平均工资,并返回,数据类型 double 类型。
注意传入的参数的数据类型最好和查询的表中对应的字段的数据类型一致,防止比较判断出现错误,返回的数据类型也尽量 >= 表中查询返回的数据类型,防止返回数据类型错误
DELIMITER $ -- 修改;分号结束符为$
CREATE FUNCTION emps_dept_avg(dept_id INT) -- 存储函数默认是 in,可以省略
RETURNS DOUBLE -- 返回的数据类型,注意多了个 s
BEGIN -- 开始
RETURN ( -- 返回值,只能有一个
SELECT AVG(salary)
FROM emps
WHERE department_id = 100);
END $ -- 使用修改后的$结束符,表示结束
DELIMITER ; -- 修改回去
调用该emps_dept_avg() 存储函数
SELECT emps_dept_avg(100);
创建完之后,怎么知道我们创建的存储过程、存储函数是否成功了呢?
MySQL存储了存储过程和函数的状态信息,用户可以使用SHOW STATUS
语句或SHOW CREATE
语句来查 看,也可直接从系统的information_schema
数据库中查询。这里介绍3种方法。
方式1:使用SHOW CREATE
语句查看存储过程创建信息
格式如下:
SHOW CREATE FUNCTION 存储函数名;
可以在DOS 命令中最后面添加上\G分行显示,看上去更加美观
举例: 查看存储函数: emps_dept_avg() 的信息
SHOW CREATE FUNCTION emps_dept_avg;
在 DOS 命令中加上 \G
方式2: 使用 SHOW STATUS
语句查看存储函数的状态信息
这个语句返回子程序的特征,如数据库、名字、类型、创建者及创建和修改日期。在不同的数据库中的存储函数也会被检查出来。
格式如下
SHOW FUNCTION STATUS LIKE '存储函数名' -- 可以使用正则表达式,模糊查询
可以使用 \G 在 DOS 命令中分行显示更加美观。
举例 :查看存储函数 emps_dept_avg() 的信息
SHOW FUNCTION STATUS LIKE 'emps_dept_avg';
在 DOS 命令中使用,添加上 \G
方式3: 从information_schema.Routines
表中查看存储过程和函数的信息
MySQL中存储过程和函数的信息存储在 information_schema
数据库下的 Routines
表中。可以通过查询该表的记录来查询存储过程和函数的信息。基本语法形式如下:
SELECT *
FROM information_schema.`ROUTINES`
WHERE routine_name = '存储函数名' AND routine_type = 'FUNCTION';
-- 指明是存储函数
同样可以在DOS 命令中 的最后面添加上 /G,用于分行显示。
SELECT *
FROM information_schema.`ROUTINES`
WHERE routine_name = '存储函数名' AND routine_type = 'FUNCTION'\G;
-- 指明是存储函数
举例: 查看存储函数 emps_dept_avg() 的信息
SELECT *
FROM information_schema.`ROUTINES`
WHERE routine_name = 'emps_dept_avg' AND routine_type = 'FUNCTION';
在 DOS 命令中使用,添加上 \G
修改存储过程或函数,不影响存储过程或函数功能,只是修改相关特性。使用ALTER
语句实现。
格式如下:
ALTER FUNCTION 存储函数名 [characteristic ...]
其中,characteristic 指定存储过程或函数的特征,其取值信息与创建存储过程,函数时的取值信息略有不同。
{ CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'
CONTAINS SQL
表示子程序包含SQL语句,但不包含读或写数据的语句。NO SQL
表示子程序中不包含SQL语句。READS SQL DATA
, 表示子程序中包含读数据的语句。MODIFIES SQL DATA
表示子程序中包含写数据的语句SQL SECURITY {DEFINER | INVOKER}
指明谁有权限来执行DEFINER
表示只有定义者自己才能够执行。INVOKER
表示调用者可以执行COMMENT 'string'
表示注释信息。修改存储过程使用ALTER PROCEDURE
语句,修改存储函数使用ALTER FUNCTION
语句,但是,这两个语句的结构是一样的,语句中的所有参数也是一样的。
举例:修改存储函数(emps_dept_avg) 中的 CountProc的定义。将读写权限改为MODIFIES SQL DATA,并指明调用者可以执行,添加上柱注释:部门的平均工资,代码如 下:
ALTER FUNCTION emps_dept_avg
MODIFIES SQL DATA
SQL SECURITY INVOKER
COMMENT '查询部门编号的平均工资';
使用SHOW FUNCTION STATUS LIKE 'emps_dept_avg';';
查询看看
SHOW FUNCTION STATUS LIKE 'emps_dept_avg';
DOS 命令下
删除存储函数,可以使用DROP
语句,其语法结构如下:
该方式,当存储函数存在,则删除,如果存储函数不存在,则不删除,同时会报错。
DROP FUNCTION 存储函数名;
我们可以添加上IF EXISTS
:如果程序或函数不存储,它可以防止发生错误,产生一个用SHOW WARNINGS查看的警告。如果该存储函数存在,则删除,如果不存在,则不删除,不会报错,但会给予警告
DROP FUNCTION IF EXISTS 存储函数名;
举例: 删除存储函数:emps_dept_avg
DROP FUNCTION IF EXISTS emps_dept_avg;
查看该存储函数是否删除
SHOW FUNCTION STATUS LIKE 'emps_dept_avg';
类别 | 关键字 | 调用语法 | 返回值 | 应用场景 |
---|---|---|---|---|
存储过程 | PROCEDURE | CALL 存储过程名( ) | 有 0 个或多个 | 一般用于数据表的更新 |
存储函数 | FUNCTION | SELECT 存储函数名( ) | 只能返回一个 | 一般用于查询结果为一个值并返回的 |
此外,存储函数可以放在查询语句中使用,而存储过程不行
。反之,存储过程的功能更加强大,包括能够执行对表的操作(比如创建表,删除表等)和事务操作,这些功能是存储函数所不具备的。
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见 !!!