【机房重构】——存储过程(Stored Procedure)

【前言】

       在学习数据库知识时,就曾经接触过存储过程,当时只是如蜻蜓点水一般,短暂的停留,并没有留下什么印象,在进行机房重构学习过程中,又重新认识了存储结构,从开始的抵触,不想用到后来逐渐了解,学着使用,思路越来越清晰,真正去做了才发现了其中的乐趣;常常说学习需要多总结,现在就是在积累,每一次的收获都要整理记录,才能留下深刻的印象,下面就来学习一下存储过程的知识吧。

【存储过程的概念】

      在采用客户机/服务器(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 Cash=@cash where cardno=@cardno"</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 offLinedate=@offdate ,offlinetime =@offtime,consumetime =@consumetime,consumecash =@consumecash ,onoffstate  =@onoffstate,ischeckout =@ischeckout where cardno =@cardno 
	<span style="background-color: rgb(51, 255, 255);">update</span> Card_Info  set cash=cash-@consumecash where cardNo=@cardno 
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、之前跟师傅交流提到过存储过程,我了解到的全部都是存储过程的优点,师傅强调了一下它的缺点,还有触发器(特殊的存储过程),忽然觉得对于知识的学习应该由点及面,一点点拓展,一点点深入,对知识的学习不满足,保持恒久的求知欲。

       微笑不足之处,请大家多多提意见~~

   


你可能感兴趣的:(存储过程,数据库,触发器,sql语句,机房重构)