[收藏] 浅谈 Mysql 存储过程🔃 与 函数📊:
Mysql 存储过程🔃 与 函数📊:
存储过程 和 存储函数说实话本人工作中不经常使用,康师傅也说 阿里开发准则 不建议使用存储过程/函数… 但这并不是咱不会的理由🙃
- 很久以前学习过,也听说了,
存储过程是mysql5.0新增的大致就像编程语言的 方法/函数一样,将复杂的sql 逻辑封装在一起, 使用的时候调用一下即可. - 应用程序无需关注内部的逻辑,只需要传入对应的参数, 就可以返回想要的结果. 要知道很多数据库 本身也是有很多方便的
函数(): IF() Count() ...
🆗 话不多说让我们开始学习吧!📚
存储过程🔃:
概述:
我们之前所学习的 MySQL 语句都是针对一个表或几个表的单条 SQL 语句,但是在数据库的实际操作中,经常会有需要多条 SQL 语句处理多个表才能完成的操作。
例如: 张三工资是否大于 所属部门的平均工资 先知道张三部门,计算平均工资,是否大于
存储过程是一组为了完成特定功能的 SQL 语句集合
- 减少网络传输量(客户端不需要把所有的 SQL 语句通过网络发给服务器)
- 减少了 SQL 语句暴露在 网上的风险,也提高了数据查询的安全性
- 简化操作,提高了sql语句的重用性,减少了开发程序员的压力
- 减少操作过程中的失误,提高效率
和视图、函数的对比:
- 它和视图有着同样的优点,清晰、安全,还可以减少网络传输量
不同的是试图是虚拟表 不会对底层数据表直接操作,而 存储过程是程序化sql 可以对 直接对底层表操作 - 存储过程和函数 类似,使用时候直接调用即可…相对于函数有一些细节
存储过程 和 函数调用方式不一样 有无返回值 参数类型 …
创建存储过程
语法分析:
# 因为存储过程中,为了区分多条SQL每个SQL需要使用 ;分号作为结束符号 |
类似于Java 的函数定义语法: 修饰符 返回类型 方法名(参数类型 参数名,....){ 方法体; } 学过编程语言的这里应该都不是难事.
存储过程名:
- 故名思意就是这个 存储过程的名字,方便调用执行:
CALL 数据库.存储过程名如果USE 数据库已经选中可以忽略数据库.
(IN|OUT|INOUT 参数名 参数类型,…) 存储过程的参数列表:
IN OUT INOUT 表示的是不同参数的,作用:
IN表示当前参数为入参
可以理解为,带入存储过程中,SQL 执行需要的参数, 如果不指定默认就是 IN类型OUT表示当前参数为出参
调用这个函数的,客户端/应用程序,可以在执行完毕之后读取这个参数,相当于 存储过程的返回值.. 一般用来存储一些 sql执行的值.INOUT当前参数既可以表示 出参 也可以表示 入参,有两种参数共有的特点.
参数名 参数类型
- 见名之意了, 就相当于Java 函数的:
形参名形参类型:可以是Mysql任意类型
当然参数列表也可以什么都没有,就是无参无返回值… 就像Java的无参方法...
[characteristics …] 可选
表示创建存储过程时指定的对存储过程的约束条件,其取值信息如下:
LANGUAGE SQL |
- LANGUAGE SQL
说明存储过程执行体是由SQL语句组成的,当前系统支持的语言为SQL - [NOT] DETERMINISTIC 指明存储过程执行的结果是否确定
DETERMINISTIC 表示结果是确定的 每次执行存储过程时,相同的输入会得到相同的输出NOT DETERMINISTIC 表示结果是不确定的 相同的输入可能得到不同的输出, 不指定,默认为NOT DETERMINISTIC
- { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } 子程序SQL限制
多选一NO SQL表示当前存储过程的子程序中不包含任何SQL语句READS SQL DATA表示当前存储过程的子程序中包含读数据的SQL语句MODIFIES SQL DATA表示当前存储过程的子程序中包含写数据的SQL语句CONTAINS SQL表示当前存储过程的子程序包含SQL语句,但是并不包含读写数据的SQL语句 系统默认指定. - SQL SECURITY { DEFINER | INVOKER } 表示存储过程的使用权限
SQL SECURITY DEFINER表示当前存储过程,只有创建者|定义者 才可以使用它 默认值SQL SECURITY INVOKER表示当前存储过程,有调用存储过程的权限用户可以使用它. COMMENT 'String'存储过程的 注释信息;
一般用于描述,注释当前存储过程的作用.
DELIMITER
因为MySQL默认的语句结束符号为分号 ;
为了避免与存储过程中SQL语句结束符相冲突,需要使用 DELIMITER改变Mysql的默认结束符.
使用DELIMITER命令时,注意避免使用反斜杠 ` 字符,因为反斜线是MySQL的转义字符. **如果你使用的可视化工具,例如 Navicat` 会自动的设置 DELIMITER 为其它符号,可以省略这个步骤…**
调用存储过程:CALL
ok 上面咱们了解了,存储过程的声明… 之后就可以去手动创建一个存储过程了…
在实际开发过程中,存储过程是:声明定义在数据库中的,开发者只需要知道这个存储过程是干啥的 需要传递什么参数... 之后就可以像调用方法一样调用它:
调用语法:CALL 存储过程名(实例参数)
- 注意,如果执行的存储过程属于其它的数据库, 需要指定数据库的名称 CALL 数据库名.存储过程名
- 或者 use 数据库 切换数据库
调用案例:
#假设有一个存储过程 selall() 无参存储过程 |
准备工作:
首先要准备一些表…. 当然可以不用和我一样…自由发挥
入门实例:
上面咱们了解了存储过程的 声明 调用 马上进入实战吧! 💪
#创建一个存储过程 selall() 无参存储过程,查看员工表 |
表名作为参数进行传递:
无论是存储过程 还是 存储函数都不支持将表面作为参数直接传输。 如果 FORM ’变量名‘ 后面直接跟着参数运行时候会将 变量名作为表面去查询 需要特殊处理.
#常规操作 |
通过 PREPARE EXECUTE 预编译执行形式进行动态操作.
ok, 到这里存储过程的大致相比已经有了一定的了解, 这里只是一些简单的例子,实际开发中会有很多更加复杂的案例需要自己发掘.
存储函数✨:
前面我们已经学习了存储过程了,Mysql中不仅有存储过程还有存储函数,二者大致相同但又有不同存储函数语法相对比较严格
创建存储函数:
-- 存储函数 和 存储过程声明语法大致也相同, |
- 参数列表:
存储函数 声明比较严格,参数只能是IN入参默认也是IN 所以可以不用声明参数 IN 更加符合编程语言的规范. - RETURNS
表示存储函数的 返回类型, 存储函数只能有一个返回值. 且必须有一个返回值. - [characteristics…]
和存储过程一样,声明对函数内部的声明约束. - BEGIN…END
和存储过程一样包含方法体,如果只有一条语句,也可以省略.
Select调用存储过程:
存储函数 其实就是和Mysql 本身内置函数基本类似,区别在于是 用户自己定义的.
# Mysql调用系统函数 |
入门案例:
# 创建无参存储函数,获取员工表的员工数量 |
注意事项:
存储过程和存储函数区别😶🌫️:
存储过程:
- 通过
CALL 存储过程名调用 - 参数类型支持
IN|OUT|INOUT正因如此支持多个返回值,同时也可以无返回值 - 执行方法体中支持
调用存储函数功能强大支持:创建表 删除表 事务操作这些都是存储函数不具备的
存储函数:
- 通过
SELECT 存储函数()调用 - 参数类型仅支持
IN默认也是如此 存储函数必须有返回值 且唯一 - 存储函数中不可以调用存储过程 不支持直接返回结果集查看,但存储函数定义完成之后可以在其他sql语句中调用,存储过程不支持.
创建存储函数中报错:
you might want to use the less safe log_bin_trust_function_creators variable 好像是Mysql版本函数默认设置问题:
- 方式一 局部定义函数设置,仅当前函数定义有效.
定义函数时候加上特定的函数特性:[NOT] DETERMINISTIC和{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA} - 方式二 全局定义,后面的所有自定义函数都是这样的
Mysql执行: SET GLOBAL log_bin_trust_function_creators = 1; 更改系统变量,
但是如果Mysql服务重启就会失效,建议直接配置在配置文件中 My.ini
存储过程和函数的查看、修改、删除
因为存储过程 和 存储函数的大部分语法都很相似就放在一起学习总结了:
查看:
MySQL存储了存储过程和函数的状态信息,
用户可以使用SHOW STATUS语句或SHOW CREATE语句来查 看,也可直接从系统的information_schema数据库中查询。当前可以通过工具直接查看。
#查看存储过程和函数的创建信息 |
修改
修改存储过程或函数,不影响存储过程或函数功能,只是修改相关特性。使用ALTER语句实现
ALTER {PROCEDURE | FUNCTION} 存储过程或函数的名 [characteristic ...] |
举例:
修改存储过程CountProc的定义。将读写权限改为MODIFIES SQL DATA,并指明调用者可以执行
ALTER PROCEDURE CountProc |
修改只能修改定义声明结构,不能更改 BEGIN---END影响功能。
删除
删除存储过程和函数,可以使用DROP语句,其语法结构如下
DROP {PROCEDURE | FUNCTION} [IF EXISTS] 存储过程或函数的名 |
存储过程高级✨:
变量:
在Mysql 存储过程|函数,中可以使用变量来存储查询 计算中间的结果数据…. 变量又分为 系统变量 用户自定义变量
系统变量
系统变量由数据库的系统定义,属于 服务器 层面
- 这些系统变量定义了当前Mysql 服务实例的属性,特征。
- 一般来说这些值要源于:
编译Mysql时候的参数Mysql配置文件my.ini也可以通过访问官网来进行查看
系统变量分为全局系统变量global 会话系统变量session
全局系统变量
** 全局系统变量一般被 global关键字修饰说明 有时候也简称为:全局变量 local变量**
全局系统变量对应的是Mysql服务 对所有的会话都会产生影响,但不能跨重启,重启之后都会回归默认值.
会话系统变量
会话系统变量一般被 session 关键字修饰,如果没有被任何修饰则默认会话级别
每一个Mysql 客户机成功链接都是对应着一次会话,而会话变量就是针对于这个客户端会话进行影响…
- 会话系统变量仅针对于当前会话(连接)有效。会话期间,当前会话对某个会话系统变量值的修 改,不会影响其他会话同一个会话系统变量的值
- 多个客户端,链接同一个Mysql服务资源,A客户端更改本地的会话系统变量配置,不会影响到B客户端的变量.
- 这些系统变量的初始值,一般都是 全局系统变量赋的值
在MySQL中有些系统变量只能是全局的,例如 max_connections 用于限制服务器的最大连接数;
有些系统变量作用域既可以是全局又可以是会话,例如 character_set_client 用于设置客户端的字符集;
有些系统变量的作用域只能是当前会话,例如 pseudo_thread_id 用于标记当前会话的 MySQL 连接 ID
系统变量🤖:
查看系统变量 |
用户自定义变量
用户变量是用户自己定义的:
- MySQL 编码规范,
MySQL 中的用户变量以 一个“@” 开头根据作用 范围不同,又分为会话用户变量和局部变量 - 会话用户变量 : 作用域和会话变量一样,只对 当前连接 会话有效
- 局部变量 : 只在 BEGIN 和 END 语句块中有效。局部变量只能在 存储过程和函数 中使用
会话用户变量的定义: 必须赋值 且 @ 符号开头 |
SET 和 DECLARE 区别:
||作用域| 定义位置| 语法:|
|-|-|-|-|
|SET| 当前会话 |会话的任何地方| 加@符号,不用指定类型|
|DEClARE| 定义它的BEGIN END中 |BEGIN END的第一句话, 值可以通过 SET 变量名 修改| 一般不用加@,需要指定类型,有默认值|
异常处理:
Mysql存储过程和存储函数都是支持 定义条件与处理程序
定义条件:
- 事先定义程序执行过程中可能遇到的问题
处理程序:
- 定义了在遇到问题时应当采取的处理方 式,并且保证存储过程或函数在遇到警告或错误时能继续执行
增强存储程序处理问题的能 力,避免程序异常停止运行
生活中会有很事情,可能会打断你本想好的计划,今天本想学习奈何游戏太好玩了我去玩游戏了.
程序也是如此,我们定义了一组存储过程,想着执行但是执行过程中报错了,我们不能简单的确定程序运行到哪里…
比如:
案例分析:
员工表字段:
创建一个存储过程,修改员工Abel 的邮箱,因为员工表的邮箱是非空的所以如果赋值NULL 会直接报错💥
#创建一个存储过程 |
工具调用和 控制台服务直接调用报错,但控制台报错信息更加多,可视化工具对错误信息进行封装
定义条件:
上面我们看到存储过程报错,我们并不能直接获取到报错的位置信息,而且Mysql返回的错误码 并不很方便我们来查看.
定义条件就是给MySQL中的错误码命名,这有助于存储的程序代码更清晰。
它将一个 错误名字 和 指定的 错误条件 关联起来这个名字可以随后被用在定义处理程序的 DECLARE HANDLER 语句中DECLARE 错误名称 CONDITION FOR 错误码(或错误条件)
Mysql错误码:
MySQL_error_code 和 sqlstate_value 都可以表示MySQL的错误
- MySQL_error_code是数值类型错误代码
- sqlstate_value是长度为5的字符串类型错误代码
- mysql错误码有两种,例如上面cmd错误:
MySQL_error_code 就是1046sqlstate_value 就是 3D000
定义条件案例:
将上面的错误码重命名 NULL**
#使用Mysql_error_code 进行定义命名 |
处理程序:
定义处理程序就相当于是,编程语言中的 try-catch 当存储过程中出现某些异常就执行什么样的操作.
DECLARE 处理方式 HANDLER FOR 错误类型 处理语句 |
处理方式: 处理方式有3个取值:CONTINUE、EXIT、UNDO
CONTINUE:表示遇到错误不处理,继续执行EXIT:表示遇到错误马上退出UNDO:表示遇到错误后撤回之前的操作。MySQL中暂时不支持这样的操作
错误类型:(即条件)可以有如下取值
SQLSTATE‘字符串错误码’ :表示长度为5的sqlstate_value类型的错误代码MySQL_error_code:匹配数值类型错误代码;错误名称:表示DECLARE ... CONDITION定义的错误条件名称,其实上面的也是服务于这里的,方便查看,不然直接看错误码确实比较头疼不方便记忆SQLWARNING:匹配所有以01开头的SQLSTATE错误代码;NOT FOUND:匹配所有以02开头的SQLSTATE错误代码;SQLEXCEPTION:匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码;
处理语句:
- 相当于
try-catch中的catch 当出现错误执行的方法… - 语句可以是 像
SET 变量 = 值这样的简单语句,也可以是使用BEGIN ... END编写的复合语句
定义条件+处理程序 完成异常处理:
在存储过程中,定义处理程序,捕获sqlstate_value值,
当遇到MySQL_error_code值为1048时,执行 CONTINUE操作,并且将@proc_value的值设置为-1
#创建存储过程 |
这里介绍了存储过程运行时候,如果出现错误如何进行捕获并处理…下面让我们了解存储过程的流程控制
流程控制:
解决复杂问题不可能通过一个 SQL 语句完成,我们需要执行多个 SQL 操作
流程控制语句的作用就是控 制存储过程中 SQL 语句的执行顺序,是我们完成复杂操作必不可少的一部分 针对于MySQL的流程控制语句主要有 3类
- 条件判断语句 :IF 语句和 CASE 语句
- 循环语句 :LOOP、WHILE 和 REPEAT 语句
- 跳转语句 :ITERATE 和 LEAVE 语句
分支结构 IF
IF 语句的语法结构是:
#就相当于是Java的 IF-ELSE IF—ELSE |
分支结构 CASE
CASE 语句的语法结构是: 不仅仅可用于存储过程和存储函数
#情况一:类似于switch |
分支结构 CASE 和 IF 案例:
个人觉得 IF CASE 都差不多,个人觉得CASE 更加结构化清晰方便查看,这里举例查看:
#传入参数 张三考试成绩 =100满分 >80优秀 >60及格 <60不及格 |
🆗 分支结构大致如此多加练习呀~
🔁循环结构之LOOP
LOOP循环语句用来重复执行某些语句,LOOP内的语句一直重复执行直到使用LEAVE子句跳出循环过程; 无论如果都会执行一次 没有条件需要手动 LEAVE 不然会陷入死循环
-- [] 可选表示定义LOOP的别名,多重循环用于指定循环结束的位置; |
测试:循环计算10以内整数和
CREATE PROCEDURE TenCount() |
🔁循环结构之WHILE
WHILE语句创建一个带条件判断的循环过程:
WHILE在执行语句执行时,首先对指定的 表达式进行判断,表达式成立,就执行循环,否则退出循环.
#基本语法 |
测试:循环计算10以内整数和
#创建存储过程 |
🔁循环结构之REPEAT
REPEAT语句创建一个带条件判断的循环过程
与WHILE循环不同的是,REPEAT 循环首先会执行一次循环- 然后在 UNTIL 中进行表达式的判断,如果满足条件就退出
- 如果条件不满足,则会 就继续执行循环,直到满足退出条件为止
#基本语法 |
测试:循环计算10以内整数和
#创建存储过程 |
跳转语句之LEAVE | ITERATE语句
LEAVE语句:
- 可以用在循环语句内,或者以 BEGIN 和 END 包裹起来的程序体内,表示跳出循环或者跳出程序体的操作. 可以把 LEAVE 理解为
break - LEAVE 可以用来退出
BEGIN,,,END循环结构的标记。
#基本语法: |
游标:
什么是游标:
游标个人觉得概念比较抽象:
官方的定义是:
- 游标是一种类似于指针一样的东西,我们sql 查询时候一次可以查询到很多数据集,我们没办法轻松的找到其中的某一条元素进行操作。
- 游标,提供了一种灵活的操作方式,让我们能够对结果集中的每一条记录 进行定位,并对指向的记录中的数据进行操作的数据结构。
游标让 SQL 这种面向集合的语言有了面向过程开发的能力
个人理解:
- 就可以把游标当作一个可以临时存储数据集的一个东西,
类似于集合,可以进行循环遍历进行操作每一行或指定条件行记录
使用游标的步骤:
游标必须在声明处理程序之前被声明,并且变量和条件还必须在声明游标或处理程序之前被声明
一般需要经历四个步骤。不同的 DBMS 中,使用游标的语法可能略有不同。
声明游标:
在MySQL中,使用DECLARE关键字来声明游标,其语法的基本形式如下:
#这个语法适用于 MySQL,SQL Server,DB2 和 MariaDB |
打开游标:
当我们定义好游标之后,如果想要使用游标,必须先打开游标
打开游标的时候 SELECT 语句的查询结 果集就会送到游标工作区,为后面游标的 逐条读取 结果集中的记录做准备。
OPEN 游标名; |
使用游标:
游标必须在声明处理程序之前被声明,并且变量和条件还必须在声明游标或处理程序之前被声明
游标是一个结果集,一个SQL结果集可以有很多列,游标可以对每一行进行操作,将没一行的列,赋值给变量上进行操作…
#语法如下: |
注意⚠️:
- 变量,必须在声明游标之前就定义好
游标的查询结果集中的字段数,必须跟 INTO 后面的变量数一致否则,在存储过程执行的时 候,MySQL 会提示错误
关闭游标:
有 OPEN 就会有 CLOSE,也就是打开和关闭游标
当我们使用完游标后需要关闭掉该游标。因为游标会占用系统资源
如果不及时关闭,游标会一直保持到存储过程结束,影响系统运行的效率。
CLOSE 游标名; |
关闭游标之后,我们就不能再检索查询结果中的数据行,如果需要检索只能再次打开游标。
游标案例
公司年会总结,之后发现 90部门 和 100部门业绩突出,给两个部门分别没人涨工资500 1000
CREATE PROCEDURE raises() |
涨薪后:一不小心执行多了~~
总结:
游标是 MySQL 的一个重要的功能,为 逐条读取 结果集中的数据 提供了完美的解决方案
**
- **游标可以在存储程序中使用,效率高,程序也更加简洁
但同时也会带来一些性能问题,比如在使用游标的过程中,会对数据行进行 加锁
- 这样在业务并发量大 的时候,不仅会影响业务之间的效率,还会
消耗系统资源 - 造成内存不足,这是因为游标是在内存中进行的处理。
建议:养成用完之后就关闭的习惯,这样才能提高系统的整体效率
关于Mysql游标报错:
No data - zero rows fetched, selected, or processed
- 网上,关于这个错误说的很多的,好像是 游标循环的时候不知道自己已经执行完毕了,FETCH没有退出循环
- 光标必须在声明处理程序之前被声明,并且变量和条件必须在声明光标或处理程序之前被声明
- 本人更新数据时发现利用游标更新数据时出错,但是数据还是可以更新的。
#加入这么一句,声明游标的指针位置,如果指针执行完毕了就给 done 赋值1 |
- 这句话就是当有游标执行结束就会,将 done 设置为0 ,如果需要同时使用多个游标
注意: 判断两游标的值,一般来说游标走完了就只能给值赋值NULL 如果值为NULL 就需要把 SET done = 0 重新刷回去!
反正就是,根据需求操作,如果有游标执行完毕,注意刷新done 以防止其他游标收到影响;
作者:Java_慈祥
链接:https://juejin.cn/post/7341642966790160419
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。





