【MySQL数据库】存储过程与自定义函数(含: SQL变量、分支语句、循环语句 和 游标、异常处理 等内容)
存储过程:一组预编译的SQL语句和流程控制语句,被命名并存储在数据库中。存储过程可以用来封装复杂的数据库操作逻辑,并在需要时进行调用。
类似的操作还有:自定义函数、.sql文件导入。
我们先从熟悉的函数开始说起:
自定义函数
User-Defined Functions(UDF)允许创建 可以在SQL查询中调用的函数,以便在执行特定操作或逻辑。
创建函数:
create function 函数名(参数名 数据类型,...) -- 参数列表
returns 返回值类型
[deterministic]
begin
-- 函数体
return 返回值;
end;
关键字:deterministic 汉译为 ‘确定的’ 。在MySQL8.0版本以上,创建函数必须在该位置添加关键字,否则会报错:
如果加上关键字deterministic的话:
另外需要注意的是:在指明函数返回值类型时,使用的关键字是 returns,在函数体内部返回值时使用的是 return 。注意区别,不要写错。
删除函数:
将自定义函数删除,语法:
drop function [if exists] myfuc;
调用函数:
使用自定义函数的方法与使用内置函数的方式一样,执行select语句:
select myfuc();
变量
变量的声明:关键字declare
declare variable datatype [default val];
-- eg.
declare age int default 18;
变量的赋值:关键字set
set variable = val;
-- eg.
set age = age + 10;
注意事项:
第一、函数的参数是已经声明好的变量,无需再次声明,可以直接使用
第二、声明变量的语句必须在函数体的最上方,其次才可以是其它语句
第三、函数体外的SQL语句也可以设置变量,语法为:【set @variable = val】
在函数体内执行sql语句为变量赋值:
-- function_head
begin
declare val int default 0;
select count(*) into val from emp;
return val;
end;
在该示例中,就将查询的结果直接赋给变量val。
结构语句
分支结构
SQL语句中一共有两种分支结构【if/case】,但这两种分支结构的语法用法却与我们熟知的语言的写法不同,但很好理解。下面我们就具体看一下:
第一种分支语句:if then...else if then...else...end if;
if condition1 then
-- coding1
else if condition2 then
-- coding2
else if condition3 then
-- coding3
else
--codingn
end if;
与我们常见的C/C++的代码作用域以花括号作为界符不同,SQL语言使用 end显式指定该作用域结束。上面定义函数时begin......end;就已经体现了。在if里面作为begin的等价关键字为:then。编译器一旦识别then就知道进入if的内部作用域了。再次识别到其它的else if或者end if,就会跳出本作用域进入下一个作用域。
第二种分支语句:case when then...when then... else... end case;
case
when condition1 then
-- exp1...;
when condition2 then
-- exp2...;
else
-- expn...;
end case;
case语句与我们熟悉的语法也不一样,不过也很好理解:遇到case直接进入分支,然后判断条件,满足即then执行作用域内的逻辑代码或表达式。最后有一个else相当于C/C++中的default,都不满足就给个默认入口。最后以end case结束分支作用域。
循环结构
同样的,循环结构也有两种语法:【while/repeat】。
第一种循环语句:while (bool) do ... end while;
while _condition_
do
--
-- coding
--
end while;
当_condition_条件满足时 do执行循环体,直到条件不满足,end while结束循环。
第二种循环语句:repeat ... until (bool) end repeat;
repeat
--
-- coding
--
until _condition_
end repeat;
与while循环不同,while循环时满足条件才执行。这种循环语句是,当满足了_condtion_条件时,会结束这个循环。
存储过程
存储过程类似于自定义函数的语法,但是有区别的。例如:无返回值、传入的参数方式不同、可以使用游标等。
基础语法
无参语法:
-- 创建存储过程
create procedure 存储过程名()
begin
-- 存储过程的逻辑代码
-- 可以包含SQL语句、控制结构和变量操作
end;
调用存储过程:
-- 执行存储过程
call procedure 存储过程名();
删除存储过程:
-- 删除存储过程
drop procedure [if exists] 存储过程名;
参数详解
create procedure 存储过程名(
[in|out|inout] 参数名 参数的数据类型,
[in|out|inout] 参数名 参数的数据类型,
......
)
begin
-- 存储过程的逻辑代码
-- sql语句、结构语句、变量操作等
end;
类型修饰符
存储过程的每个参数都有输入输出修饰,默认为:in输入参数
in(默认):输入参数,存储过程的输入值,从外部传递给存储过程,存储过程内部是只读的,不能修改它的值。【readonly】
out:输出参数,存储过程的返回值,存储过程可以修改它的值并将其返回
inout:输入和输出参数,既可以作为输入值传递给存储过程,也可以由存储过程修改并返回。
进阶语法
我们现在有一个需求,需要查询emp表,为每个员工加100元薪资。
现在遇到的难点是:emp表查询到的结果不是单一结果,只知道sal的字段类型为int,那么如何将结果集的每一条记录依次取出来呢?
我们需要学习一个新的知识:游标-cursor
!!!注意!!!
在MySQL中,存储过程允许使用游标来处理结果集,但函数里不行。可能是因为函数的设计用途不同。函数通常被要求是确定性的,或者至少在某些上下文中不允许有副作用,而游标可能涉及到对结果集的操作,可能引起非确定性的结果或者副作用。或者,函数的执行环境限制了一些操作,比如不允许修改数据库状态,而游标可能用于逐行处理,但函数需要返回单个值,这样的结构不支持
游标
在MySQL中,游标Cursor是一种数据库对象,它能够让你对结果集逐行进行处理。在操作数据库时,普通的SQL语句通常会对整个及进行操作,但在我们上述的场景下,就需要逐行的处理数据,这时有标记就能发挥作用了。
游标的使用步骤:声明=》打开=》读取=》关闭=》释放
-- 声明游标
-- 定义游标的名称 并绑定查询语句
declare cs cursor
for
select empno from emp;
游标就类似于C++实例化出来的对象:cs为对象名,对象类型为cursor-游标,绑定查询语句就是传入构造参数初始化游标的。
-- 打开游标
-- 执行定义好的查询语句 把结果集存在游标中
open cs;
联系熟悉的C++帮助理解:open 就是对象的一个成员函数,作用是执行绑定的sql语句获取结果集的。
-- 读取数据
-- 借助fetch语句从游标逐行获取数据
fetch cs into val;
fetch 就是游标的另一个成员函数:换个名字理解:getNextData-获取下一条数据,内部有一个偏移量,再次执行时,就是取结果集的下一个了。跟文件操作的偏移量联系一下也不难理解。
-- 关闭游标
-- 结束对结果集的操作后,关闭游标以释放资源
close cs;
调用内部成员函数close,相当于调用clear函数
最后还有一个释放游标,也就是从内存中移除游标的阶段。
注意事项:
- 性能问题:游标会对数据库性能产生影响,因为它是逐行处理数据的,所以在大数据集上使用时要谨慎。
- 资源管理:使用完游标后,一定要关闭并释放它,避免资源浪费。
- 异常处理:要考虑游标操作中可能出现的异常,像结果集为空或者到达结果集末尾等情况。
异常处理:错误处理-句柄
上面我们也说了,游标在使用时可能会出现异常情况。例如:循环次数过多,但数据集项数少于循环次数,那么会产生异常情况。下面给出一段代码:
create procedure mypro()
begin
declare i int default 0;
declare eid int;
declare cs cursor
for
select empno from emp;
open cs;
while i<20 do
fetch cs into eid;
select sal from emp where empno=eid;
set i = i+1;
end while;
close cs;
end;
已知,我们的emp表一共只有14条记录,那怎么从cs中取二十次数据呢?肯定会出错的,这个异常一旦出现,我们如何处理呢?SQL就提供了一种异常处理机制:句柄
句柄-处理类型:
continue - 继续执行后续代码 - 用于可恢复的错误(数据遍历结束)
exit - 退出当前代码块 - 用于不可恢复的错误(如事务冲突)
错误-条件类型:
not found - 未找到
sqlexception - sql异常
sqlwarning - sql警报
特定错误码 -如1062主键冲突
根据笛卡尔积的形式,从两个类型集合中任取一个都可以组成一个异常处理类型。
-- 声明处理句柄
declare continue handler for not found
set done=1;
declare exit handler for sqlexception
begin
rollback; -- 回滚事务
set errmsg="错误信息";
end;
declare continue handler for sqlwarning
set done=2;
感谢大家!