【前言】
在学习数据库知识时,就曾经接触过存储过程,当时只是如蜻蜓点水一般,短暂的停留,并没有留下什么印象,在进行机房重构学习过程中,又重新认识了存储结构,从开始的抵触,不想用到后来逐渐了解,学着使用,思路越来越清晰,真正去做了才发现了其中的乐趣;常常说学习需要多总结,现在就是在积累,每一次的收获都要整理记录,才能留下深刻的印象,下面就来学习一下存储过程的知识吧。
【存储过程的概念】
在采用客户机/服务器(C/S)计算模式的数据库系统中,很多工作可以在客户端完成,也可以在服务器端完成,数据库除了存放数据,还存放程序,由于这种程序以数据库对象的形式存储在数据库中,因此称为存储过程。
存储过程是使用SQL语句和流程控制语句编写的模块,存储过程经编译和优化后存储在数据库服务器端的数据库中,使用时调用即可。
存储过程中可以包含逻辑控制语句和数据操纵语句,它可以接受参数、输出参数、返回单个或多个结果集以及返回值。
我的理解:存储过程是为了完成某个特定功能的一组SQL语句,将多条相关联的SQL语句集合在一起,存放在数据库中,第一次编译之后再次调用直接调用,不需要再进行第二次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
【存储过程的使用】
1、预先了解——创建存储过程
① 打开数据库,进行如下操作:
② 参数说明:
<span style="font-family:KaiTi_GB2312;font-size:18px;">CREATE PROCEDURE <Procedure_Name, sysname, ProcedureName> (名字:指定要创建的存储过程的名称,命名必须符合命名规则,在一个数据库中或对其所有者而言,存储过程的名字必须唯一) -- Add the parameters for the stored procedure here (参数:在CREATE PROCEDURE语句中,可以声明一个或多个参数,当调用该存储过程时,用户必须给出所有的参数值,除非定义了参数的默认值) <@Param1, sysname, @p1> <Datatype_For_Param1, , int> = <Default_Value_For_Param1, , 0>, <@Param2, sysname, @p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0> AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; -- Insert statements for procedure here SELECT <@Param1, sysname, @p1>, <@Param2, sysname, @p2>(操作语句:存储过程中要包含的任意数目和类型的Transact-SQL语句) END</span>
2、具体实战——结合机房收费系统
不使用存储过程
机房收费系统的充值功能实现需要执行两条SQL语句,将充值记录插入到充值表中,并且更新卡表中的卡内金额——相关代码(D层):
<span style="font-family:KaiTi_GB2312;font-size:18px;">Public Function InsertRecharge(card As ECard, recharge As Erecharge) As Boolean Implements IRecharge.InsertRecharge Dim flag1 As Boolean Dim flag2 As Boolean Dim SqlParams As SqlParameter() = {New SqlParameter("@cardno", recharge.cardno), New SqlParameter("@addmoney", recharge.addmoney), New SqlParameter("@gdate", recharge.gdate), New SqlParameter("@gtime", recharge.gtime), New SqlParameter("@userid", recharge.userid)} '向ReCharge_Info中插入充值记录 <span style="background-color: rgb(51, 255, 255);">sql = "Insert into ReCharge_Info(CardNo,addmoney,gdate,gtime,UserID) Values(@cardno,@addmoney,@gdate,@gtime,@userid)"</span> flag1 = myHelper.AddDeleUpdate(sql, CommandType.Text, SqlParams) If flag1 = False Then flag = False Else '充值记录添加成功,更新Card_info中卡内金额 Dim SqlParams1 As SqlParameter() = {New SqlParameter("@cardno", card.cardno), New SqlParameter("@cash", card.cash)} <span style="background-color: rgb(51, 255, 255);">sql = "update Card_info set [email protected] where [email protected]"</span> flag2 = myHelper.AddDeleUpdate(sql, CommandType.Text, SqlParams1) If flag2 = False Then flag = False Else flag = True End If End If</span>
使用存储过程
机房收费系统的下机功能(部分)实现需要执行两条SQL语句,下机成功后需要更新上下机记录表和卡表——相关代码(D层):
<span style="font-family:KaiTi_GB2312;font-size:18px;">Public Function offinsert(onoff As EOnoff) As Boolean Implements IOffline.offinsert Dim sql As String Dim card As New Entity.ECard Dim sqlParams As SqlParameter() = {New SqlParameter("@cardno", onoff.cardno), New SqlParameter("@offdate", onoff.offlinedate), New SqlParameter("@offtime", onoff.offlinetime), New SqlParameter("@consumetime", onoff.consumetime), New SqlParameter("@consumecash", onoff.consumecash), New SqlParameter("@ischeckout", "未结账"), New SqlParameter("@onoffstate", "下机")} <span style="background-color: rgb(51, 255, 255);"> sql = "PRO__offline"</span> Dim flag As Boolean flag = mysqlhelper.AddDeleUpdate(sql, <span style="background-color: rgb(51, 255, 255);">CommandType.StoredProcedur</span>e, sqlParams) Return flag End Function</span>
存储过程:PRO_offline
<span style="font-family:KaiTi_GB2312;font-size:18px;">USE [charge_sys] GO /****** Object: StoredProcedure [dbo].[PRO__offline] Script Date: 2016/5/27 17:07:29 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author:<Tracy> -- Create date: <2016/5/24> -- Description: <创建下机存储过程,更新上下机记录表和卡表> -- ============================================= ALTER PROCEDURE [dbo].[PRO__offline] -- Add the parameters for the stored procedure here @cardno nvarchar(50), @offdate nvarchar(50), @offtime nvarchar(50), @consumetime nvarchar(50), @consumecash numeric(18,1), @onoffstate nvarchar(50), @ischeckout nvarchar(50) AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; -- Insert statements for procedure here <span style="background-color: rgb(51, 255, 255);">update</span> OnOff_Info set [email protected] ,offlinetime [email protected],consumetime [email protected],consumecash [email protected] ,onoffstate [email protected],ischeckout [email protected] where cardno [email protected] <span style="background-color: rgb(51, 255, 255);">update</span> Card_Info set [email protected] where [email protected] END</span>
【存储过程的优点】
⑴能实现模块化程序设计。存储过程是根据实际功能的需要创建的一个程序模块,并被存储在数据库中。以后用户要完成该功能,只要在程序中直接调用该存储过程即可,无需再编写重复的程序代码。存储过程可由数据库编程方面的专门人员创建,并可独立于程序源代码而进行修改和扩展。
⑵ 提高执行效率。当客户程序需要访问服务器上的数据时,一般要经过5个步骤:查询语句被发送到服务器→服务器编译T-SQL语句→优化产生查询执行计划→数据库引擎执行查询→执行结果发回客户程序,如果执行存储在客户端本地的T-SQL程序,那么每次执行该程序是,对于程序中的每一条语句都要经过以上5个步骤;而存储过程在创建时就被编译和优化,当存储过程第一次被执行时,SQL Server为其产生查询计划并将其保存在内存中,这样以后再调用该存储过程是就不必再进行编译,即以上5个步骤中的第2步和第3步就被省略了,这能大大改善系统的性能。
⑶增强了SQL的功能和灵活性。存储过程可以用流程控制语句编写,有很强的灵活性,能完成复杂的逻辑判断和复杂的运算。
⑷降低网络的通信量。一个需要数百行T-SQL代码的操作,如果将其创建成存储过程,那么使用一条调用存储过程的语句就可完成该操作,这样就避免了在网络上发送数百行代码,从而减少了网络负荷。
⑸减轻了程序编写的工作量。存储过程可以反复调用,并可供其他前端应用程序共享应用逻辑。
⑹间接实现安全控制功能。管理员可以不授予用户访问存储过程中涉及的表的权限,而只授予执行存储过程的权限,这样,既可以保证用户通过存储过程操纵数据库中的数据,由可以保证用户不能直接访问存储过程中涉及的表。用户通过存储过程来访问表,所能进行的操作是有限制的,从而保证了表中数据的安全性。
【存储过程的缺点】
⑴可移植性差
如果一个系统过多的使用了存储过程,那系统的业务逻辑过于依赖数据库,这样就会给系统额外的增加一层数据库中的业务逻辑层,如果开发的时候用的sql server,后来发现数据量过大,需要提高性能移植到oracle或者mysql,这样就会很麻烦,相当于把存储过程重写一遍,这是不能忍受的。
⑵性能扩展性问题
随着系统访问量的增长,系统必须进行不断地升级扩展,特别对于大型系统而言,更重要的是性能可扩展性而不是局部的性能。
处理逻辑如果全部放在存储过程里,所有的处理都在数据库服务器上进行,消耗的就是数据库服务器的CPU资源,大家知道数据库服务器由于需要较高的可靠性,通常选用的都是价格昂贵的服务器,对数据库服务器升级通常都花费很大。
如果把处理逻辑放在中间层服务器上进行,中间层服务器一般都是小型的机器,价格便宜,而且中间层服务器的CPU通常主频比数据库服务器的速度还快(比如现在8CPU的数据库服务器主频只有800M,而双CPU的刀片式服务器CPU主频已经到2.8G了),而且对于多层架构,支持中间层服务器可以增加多台机器进行负载均衡,用中间层服务器即价格便宜,扩展空间也更大。
⑶代码可复用性差
存储过程还是过程型语言,两个很相似的功能在也需要两个存储过程,因为他们是互相独立的,可以互相调用,但是不能继承等面向对象的操作,这也就增加了代码量。
【小结】
1、存储过程的使用有利有弊,我们应该根据实际情况来确定是否用存储过程,存储过程应该有选择地使用,牵涉到批量数据操作的,用存储过程较好。对于小数据量处理的事务操作,应放到中间层处理,这样系统的移植性和扩展性更好。
2、不要陷入“业务应该跟着技术走”的误区,刚开始不会用存储过程,基本数据设定表中更新记录的时候只是一条Update语句就使用了存储过程,有点本末倒置,应该是为了优化系统,实现业务而运用技术,仅仅一条sql语句其实没有必要用存储过程。
3、之前跟师傅交流提到过存储过程,我了解到的全部都是存储过程的优点,师傅强调了一下它的缺点,还有触发器(特殊的存储过程),忽然觉得对于知识的学习应该由点及面,一点点拓展,一点点深入,对知识的学习不满足,保持恒久的求知欲。
不足之处,请大家多多提意见~~