[MySQL]存储过程

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/130857854
出自【进步*于辰的博客

存储过程的细节很多,而在实际工作中又未必都能涉及这些细节,工作时间一长,就可能忘记,于是特来写这篇文章,既是为自己做个笔记,也是跟大家分享存储过程的学习和使用方法。
参考笔记三,P34.1、P35、P37.1、P66.1。

1、介绍

“存储过程”是一种存储于数据库、封装了SQL语句和流程控制语句、进而通过类似 “方法调用” \color{green}{“方法调用”} 方法调用的形式来调用(如:传参、获取返回值)、从而实现业务功能(即将一定程序业务迁移到数据库内,将业务交由数据库管理)的数据结构。

优点: \color{green}{优点:} 优点:

  1. 存储过程对复杂的SQL语句进行了封装,而调用简便,故简化了一些复杂的操作(如:业务逻辑)。
  2. 简化了对变动的管理
    若数据表变动(如:表名、字段名修改)或业务变动,不需要变动代码。
  3. 提高了程序性能
    因为存储过程存储于数据库,减少了 S Q L 传输 \color{blue}{SQL传输} SQL传输的流量(一般会用到存储过程的SQL语句都很长,几十上百行)。并且,数据库会在调用时对存储过程进行编译。大多数数据库(如:Oracle、MySQL)中,编译后的存储过程存储于数据库 缓存 \color{purple}{缓存} 缓存,其中,MySQL存储过程是 “按需编译” \color{red}{“按需编译”} 按需编译
    若存储过程在单个连接中被多次调用,调用的就是缓存内的存储过程,进一步提高了查询速度;否则会先对存储过程进行编译,此时存储过程的执行效率相当于查询。
  4. 存储过程提供了一个 接口 \color{brown}{接口} 接口供开发人员调用,这使得开发人员不必考虑其内部细节。同时,只需向访问存储过程的应用程序授权,而不必为其提供基础数据表权限,故提高了安全性,且可重用和透明。

缺点: \color{blue}{缺点:} 缺点:

  1. 存储过程会占用当前连接内存
    因为存储过程经过编译存储于缓存中,而缓存是内存的一部分。其中,由于MySQL设计的初衷是“高效的查询,非逻辑运算”,故若存储过程中使用了大量的逻辑操作则会占用大量的 C P U \color{green}{CPU} CPU
  2. 存储过程的结构使得开发复杂的存储过程变得困难。
  3. 存储过程难以调试(仅有很少的工具可以调试存储过程),使得开发和维护都不容易。
  4. 对数据库的依赖性高,难以移植(存储过程的内部就是SQL语句,自然对数据库依赖性高)。

补充说明: \color{red}{补充说明:} 补充说明:

关于流程控制语句,可查阅博文《[MySQL]流程控制语句》。

大都是情况下,存储过程内都会包含流程控制语句。为何?因为使用存储过程的原因无非两种:

  1. 封装一条复杂的SQL语句(往往是一个事务)。
  2. 封装多个原子操作(SQL语句),而这些原子操作间会进行一些逻辑运算或数据处理。

2、存储过程的使用

2.1 可视化操作(navicat)

工具:navicat。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所有存储过程存储于数据表information_schema.routines中。

2.2 示例

数据表:

员工表:emp(emp_no, emp_name, emp_salary, ...)

需求:查询工资是某员工双倍的员工的员工名。
(注:此示例非常简单,仅是向大家展示存储过程的基本使用,关键在于后续对存储过程使用的说明)

CREATE DEFINER=`root`@`localhost` PROCEDURE `P_admin_EIByENo_Sel`(IN `empNo` int,OUT `empName` varchar(20))
BEGIN
	
	// 定义变量 doubleSal,表示“双倍工资”,默认值为0,定义默认值也可以是 default(0)
	declare doubleSal int default 0;
	
	// 查询员工号为empNo的员工的工资,并将值赋予变量 doubleSal
	select emp_salary into doubleSal from emp where emp_no = empNo;
	
	set doubleSal = doubleSal * 2;// 赋值,必须使用 set。注意:此处不兼容:*=/+=
	
	// 查询工资是此员工工资双倍的员工名
	select emp_name into empName from emp where emp_salary = doubleSal;
	
	select empName;// 这是固定格式,相当于”result 变量“
	
END

说明:

  1. 创建存储过程格式:create procedure 存储过程名(参数列表) begin...end。(示例中definer...语句是指明用户、连接、数据库等)
  2. 存储过程命名规范:P_[前/后台标识]_[模块/功能简称]By[条件名简称]_Sel(Sel表示查询,Del表示删除...)
  3. 在参数empNo、empName前的in/out 参数模式 \color{green}{参数模式} 参数模式,用于声明此参数是否可用于传入 / 传出,默认为inin表示输入参数,限制参数只能用于传入,即形参out表示输出参数,限制只能用于传出,即返回值。第三种参数模式:inout,表示此参数既可传入,也可传出(具体如何使用,后续补充)。

注意

  1. 存储过程名不能包含“-”(连字符)。
  2. 参数最好不要与字段名称相同。
  3. 存储过程体必须用begin...end囊括。
  4. 每行必须以“;”(分号)结尾。
  5. 任何参数,若未初始化(设置默认值),则当将此参数作为返回值时(select 参数),此存储过程无结果。当然,select后也可是常量。
  6. 语句体(SQL语句)不能嵌套流程控制语句,如:ifloop
  7. 所有的定义(declare)必须置于开头,且变量或条件的定义必须在游标(cursor)的定义之前,且游标定义必须在continue handler之前。(注:continue handler见下文)

2.4 调用

call P_admin_EIByENo_Sel(1001, @);

这是固定格式,无论是在navicat命令行、cmd,还是在程序中。

1001对应传入参数empNo@对应传出参数empName,也可以是@empName@xx,就目前我所知,@后的标识任意(存储过程的返回值由select 变量决定,与@后的标识无关,但传出参数empName的位置必须至少有一个@(相当于占位符)。

存储过程的实参与Java方法实参有一定类似,即赋值类型限制。如示例,可以是1001,而不能是'1001'(字符型)。

3、cursor(游标)

3.1 介绍

什么是游标? \color{grey}{什么是游标?} 什么是游标?“游标”是一种能够对结果集中的每一行记录进行定位、并对所指向记录的数据进行操作的数据结构。
如:Java迭代器(iterator)中的也是游标,也称之为 “光标” \color{blue}{“光标”} 光标,其初始指向第一个元素的前面,即-1

游标的用途是什么? \color{grey}{游标的用途是什么?} 游标的用途是什么?迭代器有何用途?遍历。因此,存储过程中的游标是用于控制遍历,或者说用于在循环中获取记录。

3.2 示例

数据表:

用户表:gd_user(user_id, ...);
试题收藏表:gd_resource_collect(collect_id, user_id, ...);
试题评论表:gd_resource_comment(comment_id, user_id, ...);
评论回复表:gd_resource_response(response_id, comment_id, ...);

需求:根据用户ID,移除所有用户相关记录。

CREATE DEFINER=`root`@`localhost` PROCEDURE `P_admin_userByUId_Del`(IN `userId` int)
BEGIN
	
	declare result int default 0; /*结果*/
	declare isExp int default 0; /*是否出现异常*/
	declare tempId int; /*临时ID*/
	declare next int default 1; /*是否有下一行*/
	
	declare csCommentId cursor for select comment_id from gd_resource_comment where user_iD = userId; /*查询用户对应收藏ID*/
	declare continue handler for not found set next = 0; /*下一行处理*/------A
	declare continue handler for sqlexception begin set isExp = 1; set result = 0; end; /*异常处理*/
	
	start transaction;/*打开事务*/
	
	update gd_resource_collect set del = 1 where user_id = userId;/*移除用户对应收藏记录*/
	
	/*移除用户对应评论记录*/
	open csCommentId; /*打开游标*/
	loop1: loop
		fetch csCommentId into tempId;
		if next = 0 then
			leave loop1;
		end if;
		
		update gd_resource_response set del = 1 where comment_id = tempId; /*移除评论回复*/
		update gd_resource_comment set del = 1 where comment_id = tempId; /*移除评论*/
	end loop loop1;
	close csCommentId;

	update gd_user set del = 1 where user_id = userId; /*移除用户*/ 
	
	if isExp = 1 then /*判断是否出现异常*/
		rollback;
	else
		commit;
		set result = 1;
	end if;

	select result;

END

说明:

  1. 游标定义格式:declare 游标名 cursor for select_statement。(其中的select_statement是查询型SQL语句)
  2. 游标使用(fetch)前需要先打开(open 游标名),游标打开时如iterator的游标一般,初始指向第一行的前面。使用完后(循环结束)建议关闭游标(close 游标名)。其中,游标可多次打开。
  3. 获取游标值(下一行记录):fetch 游标名 into 变量

3.3 示例说明

1 :示例中 \color{red}{1:示例中} 1:示例中not foundnext 是做什么的? \color{red}{是做什么的?} 是做什么的?

大家用过Java迭代器的就知道,当调用next()时,在底层会先判断是否存在下一个元素,若存在,则返回此元素;否则返回null,不会出现异常。

cursor中,当fetch时,同样会先判断是否存在下一行记录,若存在则返回此记录。不同的是,若不存在,则返回当前记录(死循环)。

因此,示例中 A 的作用是,当fetch时,A 也会执行,若满足not found时(不存在下一行记录),执行set 变量 = 值。那么,就可以使用此变量退出循环,避免“死循环”。

2 :“游标可多次打开”是什么意思? \color{red}{2:“游标可多次打开”是什么意思?} 2游标可多次打开是什么意思?

这就关乎游标的定义了(declare...cursor...)。在存储过程的规范中,有这么一条:

所有的定义(declare)必须置于开头,且变量或条件的定义必须在游标(cursor)的定义之前,且游标定义必须在continue handler之前。(上文【存储过程的使用】中说道)

所以,curosr的定义也必须置于开头。不过,select_statement并不是在游标定义时就执行,而是在打开游标时。

因此,在游标再次打开时,其再次指向第一行的前面(可多次遍历)。

3 :示例中的 \color{red}{3:示例中的} 3:示例中的sqlexception 是做什么的? \color{red}{是做什么的?} 是做什么的?

在plsql中,默认情况下,DML需要手动提交或回滚。其中,可以使用保存点(savepoint)设置回滚范围。而在navicat中,由于其默认将某一条SQL都作为一个事务,所有DML都自动提交,故无法自定义回滚。

因此,可以使用start transaction;(打开事务)自定义事务范围,关闭自动提交功能。

寻常我们在navicat中执行SQL可能并不会注意这个问题,但在存储过程中则必须如此(自定义事务)。

因此,sqlexception的作用就是当存储过程中的某条SQL出现异常时,执行后面begin...end;内的内容,最后再通过某个变量判断是否出现异常,进而提交或回滚。(:存储过程中出现异常时不会停止执行)

最后

本文中的例子是为了方便大家理解、便于阐述存储过程而简单举出或是我曾用过的,不一定有实用性,仅是抛砖引玉。

其实存储过程的细节很多,只是我没有那么细致地一一进行阐述。我阐述的原则是“以吾之理解,着重之阐述”。因此,这篇文章可能并不适合 0 基础。

给大家推荐两篇博文(转发),这是我系统学习MySQL存储过程时参考的文章。

  1. MySQL中的存储过程(详细篇)
  2. Mysql存储过程大全

如果大家想要快速掌握这个知识点,我的建议是“多测试,学以致用”。

本文完结。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: SQL Server 存储过程是一组预编译的 T-SQL 语句,它们被存储在数据库中,并可以通过名称调用执行存储过程可以带参数,也可以返回结果集或输出参数。存储过程可以用于实现复杂的业务逻辑、数据访问控制等功能。 以下是一个简单的 SQL Server 存储过程示例: ``` CREATE PROCEDURE GetCustomers @City nvarchar(50) AS BEGIN SELECT * FROM Customers WHERE City = @City END ``` 以上存储过程接收一个城市名称作为参数,然后返回该城市的所有客户记录。 执行存储过程的语法如下: ``` EXEC GetCustomers 'Los Angeles' ``` 以上语句将调用名为 GetCustomers 的存储过程,并将 'Los Angeles' 作为参数传递给它。 ### 回答2: SQL Server存储过程是一组预编译的SQL语句和命令的集合,它们被打包成一个单元,然后在需要的时候被调用执行存储过程通常用于处理和管理数据库中的复杂逻辑和业务规则。 存储过程的主要优势之一是提高了数据库的性能。当存储过程被创建后,它会被编译,并且在后续的调用过程中可以重复使用编译结果,从而减少了每次执行查询所需的时间。此外,存储过程还可以降低网络流量,因为只需要向数据库发送一条命令,而不是多次发送多个查询。 另一个优势是存储过程可以更好地保护数据库的安全性。通过存储过程,可以限制用户对数据库中的特定数据和操作的访问权限。这样可以防止用户直接修改、删除或插入数据库中的数据,同时还可以记录用户对数据库的操作,以便进行审计。 此外,存储过程还有以下特点: 1. 可以接受输入参数和返回输出参数,使得存储过程可以根据不同的输入进行不同的操作,并返回结果给调用者。 2. 可以包含流程控制语句,如条件判断和循环,使得存储过程可以实现更复杂的业务逻辑。 3. 可以在一个事务中执行多个SQL语句,从而保证了数据库的一致性和完整性。 4. 可以被其他存储过程或脚本调用,实现代码的复用和模块化。 总之,SQL Server存储过程是一种强大的数据库工具,可以提高性能、保护安全性,并且具有代码复用和模块化的优点。它在处理和管理数据库中的复杂逻辑和业务规则方面发挥着重要的作用。 ### 回答3: sqlserver存储过程是一组预先编译的、可重用的SQL代码,它被存储在数据库中并可以通过名称调用执行存储过程可以接受输入参数,并返回一个或多个结果集,以满足特定的业务需求。 使用存储过程有以下几个优点: 1. 提高性能:存储过程在数据库中进行预编译,可以减少网络传输和编译时间。此外,存储过程还可以对数据库进行优化,以提高查询的执行速度。 2. 提高安全性:存储过程可以限制对数据库的直接访问,通过存储过程执行SQL语句,可以有效地防止恶意的数据操作和SQL注入攻击。 3. 降低维护成本:存储过程可以被多个应用程序共享和重用,这样可以减少代码重复的工作量。当需要对某个业务逻辑进行修改时,只需要修改存储过程的代码,而不必修改多个应用程序。 4. 支持事务处理:存储过程中可以包含事务管理的逻辑,可以确保数据的一致性和完整性。在存储过程中,可以使用BEGIN TRANSACTION、COMMIT和ROLLBACK语句来管理事务。 5. 提高可维护性:在存储过程中,可以定义变量、条件判断、循环语句等,使得存储过程更加灵活和可控。此外,存储过程还可以使用异常处理机制来捕获和处理异常情况。 总而言之,sqlserver存储过程具有提高性能、提高安全性、降低维护成本、支持事务处理以及改善可维护性的优点。使用存储过程可以有效地提升数据库应用程序的开发效率和运行效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进步·于辰

谢谢打赏!!很高兴可以帮到你!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值