第十五章:存储过程与函数
15.1:存储过程概述
-
理解
-
含义:
存储过程的英文是
Stored Procedure
。它的思想很简单,就是一组经过预先编译的SQL
语句的封装。 -
执行过程:
存储过程预先存储在
MySQL
服务器上,需要执行的时候,客户端只需要向服务端发出调用存储过程的命令,服务端就可以把预先存储好的这一系列SQL
语句全部执行。 -
好处
- 简化操作,提高了
sql
语句的重要性,减少了开发程序员的压力。 - 减少操作过程中的失误,提高效率。
- 减少网络删除量(客户端不需要把所有的
SQL
语句通过网络发给服务器)。 - 减少了
SQL
语句暴露在网上的风险,也提高了数据查询的安全性。
- 简化操作,提高了
-
-
分类
存储过程的参数类型可以是
IN
、OUT
、INOUT
。根据这点分类如下:- 没有参数(无参数无返回)
- 仅仅带
IN
类型(有参数无返回) - 仅仅带
OUT
类型(无参数有返回) - 既带
IN
又带OUT
(有参数无返回) - 带
INOUT
(有参数有返回)
注意:
IN
、OUT
、INOUT
都可以在一个存储过程中带多个。
15.2:创建存储过程
-
语法分析
-
语法
CREATE PROCEDURE 存储过程名(IN | OUT | INOUT 参数名 参数类型, ....) [characteristics ...] BEGIN 存储过程体 END
-
参数前面的符号说明
-
IN
:当前参数为输入参数,也就是表示入参。存储过程只是读取这个参数的值。如果没有定义参数种类,默认就是
IN
,表表示输入参数。 -
OUT
:当前参数为输出参数,也就是表示出参。执行完成之后,调用这个存储过程的客户端或者应用程序就可以读取这个参数返回的值了。
-
INOUT
:当前参数既可以输入参数,也可以输出参数。
-
-
形参类型可以是
MySQL
数据库中的任意类型。 -
characteristics
表示创建存储过程时指定的存储过程的约束条件,其取值信息如下:LANGUAGE SQL
:说明存储过程执行体是由SQL
语句组成的,当前系统支持的语言为SQL
。[NOT] DETERMINISTIC
:指明存储过程执行的结果是否确定。DETERMINISTIC
表示结果确定的,每次执行存储过程时,相同的输入会得到相同的输出。NOT DETERMINISTIC
表示结果是不确定的,相同的输入可能得到不同的输出。若没有指定任意一个值,默认为不确定的
- 指明子程序使用
SQL
语句的限制:{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
CONTAINS SQL
:表示当前存储过程的子程序包含SQL
语句,但是并不包含读写数据的SQL
语句。系统默认指定情况。NO SQL
表示当前存储过程的子程序中不包含任何SQL
语句。READS SQL DATA
:表示当前存储过程的子程序中包含读数据的SQL
语句。MODIFIES SQL DATA
:表示当前存储过程的子程序中包含写数据的SQL
语句。
SQL SECURITY { DEFINER | INVOKER }
:执行当前存储过程的权限,即指明哪些用户能够执行当前存储过程。DEFINER
:表示只有当前存储过程的创建者或者定义者才能执行当前存储过程。没有设置系统默认指定。INVOKER
:表示拥有当前存储过程的访问权限的用户能够执行当前存储过程。
COMMENT 'String'
:注释信息,可以用来描述存储过程。
-
存储过程体中可以有多条
SQL
语句,如果仅仅一条SQL
语句,则可以省略BEGIN
和END
。 -
需要设置新的结束标志
DELIMITER 新的结束标记
因为
MySQL
默认的语句结束符号为分号,为了避免与存储过程中SQL
语句结束符相冲突,需要使用DELIMITER
改变存储过程的结束符。
-
-
代码举例
#类型1:无参数无返回值 #举例1:创建存储过程select_all_data(),查看 employees 表的所有数据 DELIMITER $ CREATE PROCEDURE select_all_data() BEGIN SELECT * FROM employees; END $ DELIMITER ; #2. 存储过程的调用 CALL select_all_data(); #类型2:带 OUT #举例2:创建存储过程show_min_salary(),查看“emps”表的最低薪资值。并将最低薪资通过OUT参数“ms”输出 DESC employees; DELIMITER // CREATE PROCEDURE show_min_salary(OUT ms DOUBLE) BEGIN SELECT MIN(salary) INTO ms FROM employees; END // DELIMITER ; #调用 CALL show_min_salary(@ms); SELECT @ms; #类型3:带 IN #举例3:创建存储过程show_someone_salary(),查看“emps”表的某个员工的薪资,并用IN参数empname输入员工姓名。 DELIMITER // CREATE PROCEDURE show_someone_salary(IN empname VARCHAR(20)) BEGIN SELECT salary FROM employees WHERE last_name = empname; END // DELIMITER; #调用方式1 CALL show_someone_salary('Abel'); #调用方式2 SET @empname := 'Abel'; CALL show_someone_salary(@empname); #类型4:带 IN 和 OUT #举例4:创建存储过程show_someone_salary2(),查看“emps”表的某个员工的薪资,并用IN参数empname输入员工姓名,用OUT参数empsalary输出员工薪资。 DELIMITER // CREATE PROCEDURE show_someone_salary2(IN empname VARCHAR(20), OUT empsalary DECIMAL(10, 2)) BEGIN SELECT salary INTO empsalary FROM employees WHERE last_name = empname; END // DELIMITER; #调用 SET @empname = 'Abel'; CALL show_someone_salary2(@empname, @empsalary); SELECT @empsalary; #类型5:带 INOUT #举例5:创建存储过程show_mgr_name(),查询某个员工领导的姓名,并用INOUT参数“empname”输入员工姓名,输出领导的姓名。 DELIMITER $ CREATE PROCEDURE show_mgr_name(INOUT empname VARCHAR(25)) BEGIN SELECT last_name INTO empname FROM employees WHERE employee_id = ( SELECT manager_id FROM employees WHERE last_name = empname ); END $ DELIMITER; #调用 SET @empname := 'Abel'; CALL show_mgr_name(@empname); SELECT @empname;
15.3:存储函数的使用
-
语法分析
CREATE FUNCTION 函数名(参数名 参数类型, ....) RETURNS 返回值类型 [characteristics ...] BEGIN 函数体 RETURN 返回值或表达值 END
-
参数列表:指定参数为
IN
、OUT
或INOUT
只对RPOCEDURE
是合法的,FUNCTION
中总是默认为IN
参数。 -
RETURNS type
语句表示函数返回数据的类型。
RETURNS
子句只能对FUNCTION
做指定,对函数而言这是强制的。它用来指定函数的返回值类型,而且函数体必须包含一个RETUEN value
语句。 -
characteristic
创建函数时指定的对函数的约束。取值与创建存储过程相同。 -
函数体也可以用
BEGIN ... END
来表示SQL
代码的开始和结束。如果函数体只有一条语句,也可以省略BEGIN ... END
。
-
-
调用存储函数
在
MySQL
中,存储函数的使用方法与MySQL
内部函数的使用方法是一样。换言之,用户自己定义的存储函数与MySQL
内部函数是一个性质的。区别在于,存储函数是用户自己定义的,而内部函数是MySQL
的开发者定义的。SELECT 函数名(实参列表)
-
代码举例
# 举例1:创建存储函数,名称为email_by_name(),参数定义为空,该函数查询Abel的email,并返回,数据类型为字符串型。 DELIMITER // CREATE FUNCTION email_by_name() RETURNS VARCHAR(25) # 解决创建存储函数报“you might want to use the less safe log_bin_trust_function_creators variable”错方式一 DETERMINISTIC CONTAINS SQL READS SQL DATA BEGIN RETURN (SELECT email FROM employees WHERE last_name = 'Abel'); END // DELIMITER; #调用 SELECT email_by_name(); #举例2:创建存储函数,名称为email_by_id(),参数传入emp_id,该函数查询emp_id的email,并返回,数据类型为字符串型。 #解决创建存储函数报“you might want to use the less safe log_bin_trust_function_creators variable”错方式二 SET GLOBAL log_bin_trust_function_creators = 1; # 声明函数 DELIMITER // CREATE FUNCTION email_by_id(emp_id INT) RETURNS VARCHAR(25) BEGIN RETURN (SELECT email FROM employees WHERE employee_id = emp_id); END // DELIMITER; #调用 SELECT email_by_id(101); SET @emp_id := 102; SELECT email_by_id(@emp_id);
-
对比存储函数和存储过程
关键字 调用语法 返回值 应用场景 存储过程 RPOCEDURE
CALL
存储过程()
理解为有 0
个或多个一般用于更新 存储函数 FUNCTION
SELECT
函数()
只能是一个 一般用于查询结果为一个值并返回时 此外,存储函数可以放在查询语句中使用,存储过程不行。反之,存储过程的功能更加强大,包括能够执行对表的操作(比如创建表、删除表等)和事物操作,这些功能是存储函数不具备的。
15.4:存储过程和函数的查询、修改、删除
-
查看
-
使用
SHOW CREATE
语句查询存储过程和函数的创建信息# 查看存储过程 SHOW CREATE PROCEDURE show_mgr_name; # 查看存储函数 SHOW CREATE FUNCTION count_by_id;
-
使用
SHOW STATUS
语句查看存储过程和函数的状态信息# 查看所有的存储过程和存储函数 SHOW PROCEDURE STATUS; SHOW FUNCTION STATUS; # 查看其中一个或多个的存储过程和存储函数 SHOW PROCEDURE STATUS LIKE 'show_max_salary'; SHOW FUNCTION STATUS LIKE 'email_by_id';
-
从
information_schema.Routines
表中查看存储过程和函数的信息# 查看存储过程 SELECT * FROM information_schema.`ROUTINES` WHERE ROUTINE_NAME = 'show_min_salary' AND ROUTINE_TYPE = 'PROCEDURE'; # 查看存储函数 SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_NAME = 'email_by_id' AND ROUTINE_TYPE = 'FUNCTION';
-
-
修改
修改存储过程或函数,不影响存储过程或函数功能,只是修改相关特性。使用
ALTER
语句实现。# 语法 ALTER {PROCEDURE | FUNCTION} 存储过程或函数的名 [characteristic ...] # 举例 ALTER PROCEDURE show_max_salary SQL SECURITY INVOKER COMMENT '查询最高工资';
-
删除
删除存储过程或函数,可以使用
DROP
语句,其语法结构如下:DROP {PROCEDURE | FUNCTION} [IF EXISTS] 存储过程或函数的名 # 举例 DROP FUNCTION IF EXISTS count_by_id; DROP PROCEDURE IF EXISTS show_min_salary;
-
存储过程的优点和缺点
- 优点
- 存储过程可以一次编译多次使用。
- 可以减少开发工作量。
- 存储过程的安全性强。
- 可以减少网络传输。
- 良好的封装性。
- 缺点
- 可移植性差。
- 调试困难。
- 存储过程的版本管理很困难。
- 它不适合高并发的场景。
- 优点