zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

MySQL基础-存储过程与函数

mysql存储基础 函数 过程
2023-09-27 14:22:51 时间

MySQL基础-存储过程与函数

MySQL从5.0版本开始支持存储过程和函数

存储过程和函数能够将复杂的SQL逻辑封装在一起,应用程序无须关注存储过程和函数内部复杂的SQL逻辑,而只需要简单地调用存储过程和函数即可

一、存储过程

1、概述

存储过程是一组经过 预先编译 的 SQL 语句的封装,预先存储在 MySQL 服务器上,需要执行的时候,客户端只需要向服务器端发出调用存储过程的命令,服务器端就可以把预先存储好的这一系列 SQL 语句全部执行

  • 好处:
  1. 简化操作,提高了sql语句的重用性,减少了开发程序员的压力
  2. 减少操作过程中的失误,提高效率
  3. 减少网络传输量(客户端不需要把所有的 SQL 语句通过网络发给服务器)
  4. 减少了 SQL 语句暴露在网上的风险,也提高了数据查询的安全性
  • 和视图、函数的对比:
  1. 它和视图有着同样的优点,清晰、安全,还可以减少网络传输量;不过它和视图不同,视图是 虚拟表 ,通常不对底层数据表直接操作,而存储过程是程序化的 SQL,可以 直接操作底层数据表 ,相比于面向集合的操作方式,能够实现一些更复杂的数据处理
  2. 一旦存储过程被创建出来,使用它就像使用函数一样简单,我们直接通过调用存储过程名即可;相较于函数,存储过程是 没有返回值 的
  • 存储过程的参数类型:

参数 IN 类型:输入参数(值)

参数 OUT 类型:输出参数(值)

参数 INOUT 类型:即输入又输出参数(值)

注意:IN、OUT、INOUT 都可以在一个存储过程中带多个或者没有

2、存储过程使用

  • 创建语法:
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...]
BEGIN
    存储过程体
END
  1. 形参类型可以是 MySQL数据库中的任意类型
  • characteristics 表示创建存储过程时指定的对存储过程的约束条件:
LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'
  • characteristics取值说明:
LANGUAGE SQL :说明存储过程执行体是由SQL语句组成的,当前系统支持的语言为SQL
[NOT] DETERMINISTIC :指明存储过程执行的结果是否确定。DETERMINISTIC表示结果是确定
的。每次执行存储过程时,相同的输入会得到相同的输出。NOT DETERMINISTIC表示结果是不确定的,相同的输入可能得到不同的输出。如果没有指定任意一个值,默认为NOT DETERMINISTIC。
{ CONTAINS SQL | NO 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 SECURITY { DEFINER | INVOKER } :执行当前存储过程的权限,即指明哪些用户能够执
行当前存储过程。
DEFINER 表示只有当前存储过程的创建者或者定义者才能执行当前存储过程;
INVOKER 表示拥有当前存储过程的访问权限的用户能够执行当前存储过程。  
COMMENT 'string' :注释信息,可以用来描述存储过程。
  1. 存储过程体中可以有多条 SQL 语句
1. BEGIN…END:BEGIN…END 中间包含了多个语句,每个语句都以(;)号为结束符。
2. DECLARE:DECLARE 用来声明变量,使用的位置在于 BEGIN…END 语句中间,而且需要在其他语句使用之前进行变量的声明。
3. SET:赋值语句,用于对变量进行赋值。
4. SELECT… INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值。
  1. 设置新的结束标记
DELIMITER 新的结束标记
#因为MySQL默认的语句结束符号为分号‘;’。为了避免与存储过程中SQL语句结束符相冲突,需要使用
#DELIMITER改变存储过程的结束符。
  • 示例:
DELIMITER $
CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...)
[characteristics ...]
BEGIN
    sql语句1;
    sql语句2;
END $
DELIMITER ;
  • CALL语句调用存储过程:
CALL 存储过程名(实参列表)
  • 设置变量输入输出值:
SET @name=值;
CALL sp1(@name);
SELECT @name;
  • 案例:创建存储过程show_mgr_name(),查询某个员工领导的姓名,并用INOUT参数“empname”输入员工姓名,输出领导的姓名
DELIMITER //
CREATE PROCEDURE show_mgr_name(INOUT lname VARCHAR(20))
BEGIN
    SELECT m.last_name INTO lname 
    FROM employees m
    WHERE m.employee_id = (
    SELECT e.employee_id 
    FROM employees e
    WHERE lname=e.last_name);
END //
DELIMITER ;

image-20230330215816038

二、存储函数

1、概述

MySQL支持自定义函数,定义好之后,调用方式与调用MySQL预定义的系统函数一样

用户自己定义的存储函数与MySQL内部函数是一个性质的

区别在于,存储函数是 用户自己定义 的,而内部函数是MySQL的 开发者定义 的

2、存储函数使用

  • 创建语法:
CREATE FUNCTION 函数名(参数名 参数类型,...)
RETURNS 返回值类型
[characteristics ...]
BEGIN
    函数体 #函数体中肯定有 RETURN 语句
END
  • 说明:

1、参数列表:指定参数为IN、OUT或INOUT只对PROCEDURE是合法的,FUNCTION中总是默认为IN参数

2、RETURNS type 语句表示函数返回数据的类型,函数体必须包含一个 RETURN value 语句

3、characteristic 创建函数时指定的对函数的约束,取值与创建存储过程时相同

4、函数体也可以用BEGIN…END来表示SQL代码的开始和结束。如果函数体只有一条语句,也可以省略BEGIN…END

  • 调用存储函数:
SELECT 函数名(实参列表)

存储函数的使用方法与MySQL内部函数的使用方法是一样的,用户自己定义的存储函数与MySQL内部函数是一个性质的

  • 案例:创建存储函数count_by_id(),参数传入dept_id,该函数查询dept_id部门的员工人数,并返回,数据类型为整型
DELIMITER //
CREATE FUNCTION count_by_id(dept_id INT)
RETURNS INT
LANGUAGE SQL
NOT DETERMINISTIC
READS SQL DATA
SQL SECURITY DEFINER
COMMENT '查询部门平均工资'
BEGIN
RETURN (SELECT COUNT(*) 
	FROM employees 
	WHERE department_id = dept_id);
END //
DELIMITER ;

image-20230330221105022

注意:若在创建存储函数中报错“ you might want to use the less safe log_bin_trust_function_creators variable ”,有两种处理方法:

  • 方式1:加上必要的函数特性
“[NOT] DETERMINISTIC”和“{CONTAINS SQL | NO SQL | READS SQL DATA |MODIFIES SQL DATA}”
  • 方式2:
SET GLOBAL log_bin_trust_function_creators = 1;

三、查看修改删除

  • 使用SHOW CREATE语句查看创建信息 :
SHOW CREATE {PROCEDURE | FUNCTION} 存储过程名或函数名
  • 使用SHOW STATUS语句查看状态信息:
SHOW {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern']
  • 从information_schema.Routines表中查看存储过程和函数的信息:
SELECT * FROM information_schema.Routines
WHERE ROUTINE_NAME='存储过程或函数的名' [AND ROUTINE_TYPE = {'PROCEDURE|FUNCTION'}];

说明:如果在MySQL数据库中存在存储过程和函数名称相同的情况,最好指定ROUTINE_TYPE查询条件来指明查询的是存储过程还是函数

  • 使用ALTER语句实现修改相关特性 :
ALTER {PROCEDURE | FUNCTION} 存储过程或函数的名 [characteristic ...]
  • DROP语句删除存储过程和函数:
DROP {PROCEDURE | FUNCTION} [IF EXISTS] 存储过程或函数的名

四、存储过程与函数总结

1、对比

关键字调用语法返回值应用场景
存储过 程PROCEDURECALL 存储过 程()理解为有0个或 多个一般用于更新
存储函 数FUNCTIONSELECT 函数 ()只能是一个一般用于查询结果为一个值并 返回时

2、存储过程优缺点

  • 优点:

1、存储过程可以一次编译多次使用。存储过程只在创建时进行编译,之后的使用都不需要重新编译,这就提升了 SQL 的执行效率。

2、可以减少开发工作量。将代码 封装 成模块,实际上是编程的核心思想之一,这样可以把复杂的问题拆解成不同的模块,然后模块之间可以 重复使用 ,在减少开发工作量的同时,还能保证代码的结构清晰。

3、存储过程的安全性强。我们在设定存储过程的时候可以 设置对用户的使用权限 ,这样就和视图一样具有较强的安全性。

4、可以减少网络传输量。因为代码封装到存储过程中,每次使用只需要调用存储过程即可,这样就减少了网络传输量。

5、良好的封装性。在进行相对复杂的数据库操作时,原本需要使用一条一条的 SQL 语句,可能要连接多次数据库才能完成的操作,现在变成了一次存储过程,只需要 连接一次即可 。

  • 缺点:

1、可移植性差。存储过程不能跨数据库移植,比如在 MySQL、Oracle 和 SQL Server 里编写的存储过程,在换成其他数据库时都需要重新编写。

2、调试困难。只有少数 DBMS 支持存储过程的调试。对于复杂的存储过程来说,开发和维护都不容易。虽然也有一些第三方工具可以对存储过程进行调试,但要收费。

3、存储过程的版本管理很困难。比如数据表索引发生变化了,可能会导致存储过程失效。我们在开发软件的时候往往需要进行版本管理,但是存储过程本身没有版本控制,版本迭代更新的时候很麻烦。

4、它不适合高并发的场景。高并发的场景需要减少数据库的压力,有时数据库会采用分库分表的方式,而且对可扩展性要求很高,在这种情况下,存储过程会变得难以维护, 增加数据库的压力 ,显然就不适用了。

重新编写。

2、调试困难。只有少数 DBMS 支持存储过程的调试。对于复杂的存储过程来说,开发和维护都不容易。虽然也有一些第三方工具可以对存储过程进行调试,但要收费。

3、存储过程的版本管理很困难。比如数据表索引发生变化了,可能会导致存储过程失效。我们在开发软件的时候往往需要进行版本管理,但是存储过程本身没有版本控制,版本迭代更新的时候很麻烦。

4、它不适合高并发的场景。高并发的场景需要减少数据库的压力,有时数据库会采用分库分表的方式,而且对可扩展性要求很高,在这种情况下,存储过程会变得难以维护, 增加数据库的压力 ,显然就不适用了。