liyihongcug 发表于 2010-10-25 17:06

什么时候使用存储过程比较适合

什么时候使用存储过程比较适合



总结:
当一个事务涉及到多个SQL语句时或者涉及到对多个表的操作时就要考虑用存储过程;当在一个事务的完成需要很复杂的商业逻辑时(比如,对多个数据的操作,对多个状态的判断更改等)要考虑;还有就是比较复杂的统计和汇总也要考虑,但是过多的使用存储过程会降低系统的移植性。



为了系统的控制方便,例如当系统进行调整时,这是只需要将后台存储过程进行更改,而不需要更改客户端程序。也无需重新安装客户端应用程序。

存储过程不仅仅适用于大型项目,对于中小型项目,使用存储过程也是非常有必要的。其威力和优势主要体现在:
1.存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般 SQL 语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
2.当对数据库进行复杂操作时(如对多个表进行 Update,Insert,Query,Delete 时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。这些操作,如果用程序来完成,就变成了一条条的 SQL 语句,可能要多次连接数据库。而换成存储,只需要连接一次数据库就可以了。
3.存储过程可以重复使用,可减少数据库开发人员的工作量。
4.安全性高,可设定只有某此用户才具有对指定存储过程的使用权。

优点:
1.速度快。尤其对于较为复杂的逻辑,减少了网络流量之间的消耗
我有的过程和函数达到了几百行,一个微型编译器,相信用程序就更麻烦了。
2.写程序简单,采用存储过程调用类,调用任何存储过程都只要1-2行代码。
(我不知道别人怎么调用,我是深受其益)
3.升级、维护方便
4.调试其实也并不麻烦,可以用查询分析器
5.如果把所有的数据逻辑都放在存储过程中,那么asp.net只需要负责界面的显示阿什么的,出错的可能性最大就是在存储过程。我碰到的就一般是这种情况。

缺点:
1.可移植性差,我一直采用sql server开发,可是如果想卖自己的东西,发现自己简直就是在帮ms卖东西,呵呵。想换成mysql,确实移植麻烦。
2.采用存储过程调用类,需要进行两次调用操作,一次是从sql server中取到过程的参数信息,并且建立参数;第二次才是调用这个过程。多了一次消耗。
不过这个缺点可以在项目开发完成,过程参数完全确定之后,把所有过程参数信息倒入到一个xml文件中来提高性能。



当一个业务同时对多个表进行处理的时候采用存储过程比较合适。



   1. 使用存储过程在一般情况下会提高性能,因为数据库优化了存储过程的数据访问计划并应用缓存方便以后的查询;
   2. 存储过程单独保护存在于数据库中。客户端可以获取权限执行存储过程,而不需要对底层的具体表设置其他的访问权限;
   3. 存储过程会使得维护起来更加方便,因为通常修改一个存储过程要比在一个已经发布的组件中修改SQL语句更加方便;
   4. 存储过程给底层数据格式增添了额外的抽象层。使得使用存储过程的客户端对存储过程的实现细节以及对底层数据格式是隔离独立的;
   5. 存储过程能够缓解网络带宽,因为可以批量执行SQL语句而不是从客户端发送超负载的请求。

复杂的数据处理用存储过程,如有些报表处理

多条件多表联合查询,并做分页处理
SQL Server中存储过程比直接运行SQL语句慢的原因
在很多的资料中都描述说SQLSERVER的存储过程较普通的SQL语句有以下优点:

1.       存储过程只在创造时进行编译即可,以后每次执行存储过程都不需再重新编译,而我们通常使用的SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。

2.       经常会遇到复杂的业务逻辑和对数据库的操作,这个时候就会用SP来封装数据库操作。当对数据库进行复杂操作时(如对多个表进行 Update,Insert,Query,Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。可以极大的提高数据库的使用效率,减少程序的执行时间,这一点在较大数据量的数据库的操作中是非常重要的。在代码上看,SQL语句和程序代码语句的分离,可以提高程序代码的可读性。

3.       存储过程可以设置参数,可以根据传入参数的不同重复使用同一个存储过程,从而高效的提高代码的优化率和可读性。

4.       安全性高,可设定只有某此用户才具有对指定存储过程的使用权存储过程的种类:

A.       系统存储过程:以sp_开头,用来进行系统的各项设定.取得信息.相关管理工作,如 sp_help就是取得指定对象的相关信息。

B.       扩展存储过程 以XP_开头,用来调用操作系统提供的功能
exec master..xp_cmdshell 'ping 10.8.16.1'

C.       用户自定义的存储过程,这是我们所指的存储过程常用格式

    模版:Create procedure procedue_name [@parameter data_type]
    {recompile|encryption} as sql_statement

    解释:output:表示此参数是可传回的

with {recompile|encryption} recompile:表示每次执行此存储过程时都重新编译一次;encryption:所创建的存储过程的内容会被加密。



   但是最近我们项目组中有人写了一个存储过程,其计算时间为1个小时47分钟,而有的时候运行时间都超过了两个小时,同事描述说如果将存储过程中的语句拿出来直接运行也就10分钟左右就运行完毕,我没当回事,但是今天我自己写的存储过程也遇到了这个问题,在查找资料后原因终于找到了原因,原来是Parameter sniffing问题。

    下面看我是如何将运行一个小时以上的存储过程优化成在一分钟之内完成的:

原存储过程

CREATE PROCEDURE .

@THEDATE VARCHAR(30)

AS

BEGIN

    IF @THEDATE IS NULL

    BEGIN

       SET @THEDATE=CONVERT(VARCHAR(30),GETDATE()-1,112);

    END





    DELETE FROM RPT_IM_USERINFO_DAILY WHERE THEDATE=@THEDATE;



    INSERT RPT_IM_USERINFO_DAILY (THEDATE,ALLUSER,NEWUSER)

    SELECT AA.THEDATE,ALLUSER,NEWUSER

    FROM

    ( ( SELECT THEDATE,COUNT(DISTINCT USERID) ALLUSER

       FROM FACT

       WHERE THEDATE=@THEDATE

      GROUP BY THEDATE

       ) AA

       LEFT JOIN

       (SELECT THEDATE,COUNT(DISTINCT USERID) NEWUSER

      FROM FACT T1

      WHERE NOT EXISTS(

                         SELECT 1

                         FROM FACT T2

                         WHERE T2.THEDATE<@THEDATE

                           AND T1.USERID=T2.USERID)

            AND T1.THEDATE=@THEDATE

      GROUP BY THEDATE

      ) BB

       ON AA.THEDATE=BB.THEDATE);

GO

每日执行:exec pro_ImAnalysis_daily @thedate=null
耗时:1小时47分~2小时13分

经 过查找资料,原因如下(由于源文是一篇英文,有些地方写的我不是特别清楚,原文见http://groups.google.com/group /microsoft.public.sqlserver.server/msg/ad37d8aec76e2b8f?hl=en&lr=& amp;ie=UTF-8&oe=UTF-8):

    在SQL Server中有一个叫做 “Parameter sniffing”的特性。SQL Server在存储过程执行之前都会制定一个执行计划。在上面的例子中,SQL在编译的时候并不知道@thedate的值是多少,所以它在执行执行计划的时候就要进行大量的猜测。假设传递给@thedate的参数大部分都是非空字符串,而FACT表中有40%的thedate字段都是null,那么SQL Server就会选择全表扫描而不是索引扫描来对参数@thedate制定执行计划。全表扫描是在参数为空或为0的时候最好的执行计划。但是全表扫描严重影响了性能。

    假设你第一次使用了Exec pro_ImAnalysis_daily @thedate=’20080312’那么SQL Server就会使用20080312这个值作为下次参数@thedate的执行计划的参考值,而不会进行全表扫描了,但是如果使用@thedate=null,则下次执行计划就要根据全表扫描进行了。

    有两种方式能够避免出现“Parameter sniffing”问题:

(1)通过使用declare声明的变量来代替参数:使用set @variable=@thedate的方式,将出现@thedate的sql语句全部用@variable来代替。

(2) 将受影响的sql语句隐藏起来,比如:

a)      将受影响的sql语句放到某个子存储过程中,比如我们在@thedate设置成为今天后再调用一个字存储过程将@thedate作为参数传入就可以了。

b)      使用sp_executesql来执行受影响的sql。执行计划不会被执行,除非sp_executesql语句执行完。

c)      使用动态sql(”EXEC(@sql)”来执行受影响的sql。

采用(1)的方法改造例子中的存储过程,如下:

    ALTER PROCEDURE .

@var_thedate VARCHAR(30)



AS

BEGIN

    declare @THEDATE VARCHAR(30)

    IF @var_thedate IS NULL

    BEGIN

       SET @var_thedate=CONVERT(VARCHAR(30),GETDATE()-1,112);

    END





    SET @THEDATE=@var_thedate;

    DELETE FROM RPT_IM_USERINFO_DAILY WHERE THEDATE=@THEDATE;



   INSERT RPT_IM_USERINFO_DAILY (THEDATE,ALLUSER,NEWUSER)

    SELECT AA.THEDATE,ALLUSER,NEWUSER

    FROM

    ( ( SELECT THEDATE,COUNT(DISTINCT USERID) ALLUSER

       FROM FACT

       WHERE THEDATE=@THEDATE

      GROUP BY THEDATE

       ) AA

       LEFT JOIN

       (SELECT THEDATE,COUNT(DISTINCT USERID) NEWUSER

      FROM FACT T1

      WHERE NOT EXISTS(

                         SELECT 1

                         FROM FACT T2

                         WHERE T2.THEDATE<@THEDATE

                           AND T1.USERID=T2.USERID)

            AND T1.THEDATE=@THEDATE

      GROUP BY THEDATE

      ) BB

       ON AA.THEDATE=BB.THEDATE);

GO



测试执行速度为10分钟,我又检查了一下这个SQL,发现这个SQL有问题,这个SQL使用了not exists,在一个大表里面使用not exists是不太明智的,所以,我又对这个sql进行了改进,改成如下:

    ALTER PROCEDURE .

@var_thedate VARCHAR(30)



AS

BEGIN

    declare @THEDATE VARCHAR(30)

    IF @var_thedate IS NULL

    BEGIN

       SET @var_thedate=CONVERT(VARCHAR(30),GETDATE()-1,112);

    END





    SET @THEDATE=@var_thedate;

    DELETE FROM RPT_IM_USERINFO_DAILY WHERE THEDATE=@THEDATE;



    INSERT RPT_IM_USERINFO_DAILY(THEDATE,ALLUSER,NEWUSER)

    select @thedate as thedate,

         count(distinct case when today>0 then userid else null end) as alluser,

         count(distinct case when dates=0 then userid else null end) as newuser

    from

    (

       select userid,

            count(CASE WHEN thedate>=@thedate then null else thedate end) as dates,

            count(case when thedate=@thedate then thedate else null end) as today

       from   FACT

       group by userid

    )as fact

GO

测试结果为30ms以下。

from:http://blog.csdn.net/emili/archive/2008/03/17/2192081.aspx
Tag标签: SQLServer,SQL,SQL Server

http://www.cnblogs.com/wuming/archive/2009/05/06/1450980.html

liyihongcug 发表于 2010-10-25 17:07

SQL Server日志文件总结及日志满的处理
发布时间:2009.03.25 04:39   来源:赛迪网    作者:Dudu

【赛迪网-IT技术报道】事务日志(Transaction logs)是数据库结构中非常重要但又经常被忽略的部分。由于它并不像数据库中的schema那样活跃,因此很少有人关注事务日志。  

事务日志是针对数据库改变所做的记录,它可以记录针对数据库的任何操作,并将记录结果保存在独立的文件中。对于任何每一个事务过程,事务日志都有非常全面的记录,根据这些记录可以将数据文件恢复成事务前的状态。从事务动作开始,事务日志就处于记录状态,事务过程中对数据库的任何操作都在记录范围,直到用户点击提交或后退后才结束记录。每个数据库都拥有至少一个事务日志以及一个数据文件。  

出于性能上的考虑,SQL Server将用户的改动存入缓存中,这些改变会立即写入事务日志,但不会立即写入数据文件。事务日志会通过一个标记点来确定某个事务是否已将缓存中的数据写入数据文件。当SQL Server重启后,它会查看日志中最新的标记点,并将这个标记点后面的事务记录抹去,因为这些事务记录并没有真正的将缓存中的数据写入数据文件。这可以防止那些中断的事务修改数据文件。

维护事务日志  

因为很多人经常遗忘事务日志,因此它也会给系统带来一些问题。随着系统的不断运行,日志记录的内容会越来越多,日志文件的体积也会越来越大,最终导致可用磁盘空间不足。除非日常工作中经常对日志进行清理,否则日志文件最终会侵占分区内的全部可用空间。日志的默认配置为不限容量,如果以这种配置工作,它就会不断膨胀,最终也会占据全部可用空间。这两种情况都会导致数据库停止工作。  

对事务日志的日常备份工作可以有效的防止日志文件过分消耗磁盘空间。备份过程会将日志中不再需要的部分截除。截除的方法是首先把旧记录标记为非活动状态,然后将新日志覆盖到旧日志的位置上,这样就可以防止事务日志的体积不断膨胀。如果无法对日志进行经常性的备份工作,最好将数据库设置为"简单恢复模式"。在这种模式下,系统会强制事务日志在每次记录标记点时,自动进行截除操作,以新日志覆盖旧日志。  

截除过程发生在备份或将旧标记点标为非活动状态时,它使得旧的事务记录可以被覆盖,但这并不会减少事务日志实际占用的磁盘空间。就算不再使用日志,它依然会占据一定的空间。因此在维护时,还需要对事务日志进行压缩。压缩事务日志的方法是删除非活动记录,从而减少日志文件所占用的物理硬盘空间。  

通过使用DBCC SHRINKDATABASE语句可以压缩当前数据库的事务日志文件,DBCC SHRINKFILE语句用来压缩指定的事务日志文件,另外也可以在数据库中激活自动压缩操作。当压缩日志时,首先会将旧记录标记为非活动状态,然后将带有非活动标记的记录彻底删除。根据所使用的压缩方式的不同,你可能不会立即看到结果。在理想情况下,压缩工作应该选在系统不是非常繁忙的时段进行,否则有可能影响数据库性能。  

恢复数据库  

事务记录备份可以用来将数据库恢复到某一指定状态,但事务记录备份本身不足以完成恢复数据库的任务,还需要备份的数据文件参与恢复工作。恢复数据库时,首先进行的是数据文件的恢复工作。在整个数据文件恢复完成前,不要将其设为完成状态,否则事务日志就不会被恢复。当数据文件恢复完成,系统会通过事务日志的备份将数据库恢复成用户希望的状态。如果在数据库最后一次备份后,存在多个日志文件的备份,备份程序会按照它们建立的时间依次将其恢复。  

另一种被称为log shipping的过程可以提供更强的数据库备份能力。当log shipping配置好后,它可以将数据库整个复制到另一台服务器上。在这种情况下,事务日志也会定期发送到备份服务器上供恢复数据使用。这使得服务器一直处于热备份状态,当数据发生改变时它也随之更新。另一个服务器被称作监视(monitor)服务器,可以用来监视按规定时间间隔发送的shipping 信号。如果在规定时间内没有收到信号,监视服务器会将这一事件记录到事件日志。这种机制使得log shipping经常成为灾难恢复计划中使用的方案。  

性能优化  

事务日志对数据库有重要作用,同时它对系统的整体性能也有一定影响。通过几个选项,我们可以对事务日志的性能进行优化。由于事务日志是一个连续的磁盘写入过程,在这当中不会发生读取动作。因此将日志文件放在一个独立的磁盘,对优化性能有一定作用。  

另一项优化措施与日志文件的体积有关。我们可以设置日志文件的体积不超过硬盘空间的百分之几,或者确定它的大小。如果将其设置的过大会浪费磁盘空间,而如果设置的过小则会强制记录文件不断尝试扩展,导致数据库性能下降。  

事务日志文件Transaction Log File是用来记录数据库更新情况的文件,扩展名为ldf。  

在 SQL Server 7.0 和 SQL Server 2000 中,如果设置了自动增长功能,事务日志文件将会自动扩展。  

一般情况下,在能够容纳两次事务日志截断之间发生的最大数量的事务时,事务日志的大小是稳定的,事务日志截断由检查点或者事务日志备份触发。  

然而,在某些情况下,事务日志可能会变得非常大,以致用尽空间或变满。通常,在事务日志文件占尽可用磁盘空间且不能再扩展时,您将收到如下错误消息:  

Error:9002, Severity:17, State:2  

The log file for database '%.*ls' is full.  

除了出现此错误消息之外,SQL Server 还可能因为缺少事务日志扩展空间而将数据库标记为 SUSPECT。有关如何从此情形中恢复的其他信息,请参见 SQL Server 联机帮助中的“磁盘空间不足”主题。  

另外,事务日志扩展可能导致下列情形:

· 非常大的事务日志文件。

· 事务可能会失败并可能开始回滚。

· 事务可能会用很长时间才能完成。

· 可能发生性能问题。

· 可能发生阻塞现象。  

原因  

事务日志扩展可能由于以下原因或情形而发生:

· 未提交的事务

· 非常大的事务

· 操作:DBCC DBREINDEX 和 CREATE INDEX

· 在从事务日志备份还原时

· 客户端应用程序不处理所有结果

· 查询在事务日志完成扩展之前超时,您收到假的“Log Full”错误消息

· 未复制的事务

解决方法:  

日志文件满而造成SQL数据库无法写入文件时,可用两种方法:

一种方法:清空日志。

1.打开查询分析器,输入命令

DUMP TRANSACTION 数据库名 WITH NO_LOG

2.再打开企业管理器--右键你要压缩的数据库--所有任务--收缩数据库--收缩文件--选择日志文件--在收缩方式里选择收缩至XXM,这里会给出一个允许收缩到的最小M数,直接输入这个数,确定就可以了。

另一种方法有一定的风险性,因为SQL SERVER的日志文件不是即时写入数据库主文件的,如处理不当,会造成数据的损失。

1.分离数据库 企业管理器->服务器->数据库->右键->分离数据库

2:删除LOG文件

附加数据库 企业管理器->服务器->数据库->右键->附加数据库

此法生成新的LOG,大小只有500多K。  

注意:建议使用第一种方法。  

如果以后,不想要它变大。

SQL2000下使用:

在数据库上点右键->属性->选项->故障恢复-模型-选择-简单模型。

或用SQL语句:

alter database 数据库名 set recovery simple

另外,如上图中数据库属性有两个选项,与事务日志的增长有关:

Truncate log on checkpoint  

(此选项用于SQL7.0,SQL 2000中即故障恢复模型选择为简单模型)  

当执行CHECKPOINT 命令时如果事务日志文件超过其大小的70% 则将其内容清除在开发数据库时时常将此选项设置为True Auto shrink定期对数据库进行检查当数据库文件或日志文件的未用空间超过其大小的25%时,系统将会自动缩减文件使其未用空间等于25% 当文件大小没有超过其建立时的初始大小时不会缩减文件缩减后的文件也必须大于或等于其初始大小对事务日志文件的缩减只有在对其作备份时或将 Truncate log on checkpoint 选项设为True 时才能进行。  

注意:一般立成建立的数据库默认属性已设好,但碰到意外情况使数据库属性被更改,请用户清空日志后,检查数据库的以上属性,以防事务日志再次充满

赛迪网

asp.net调用存储过程方法新解
2010年8月23日 分类: ASP.NET

在使用.net的过程中,数据库访问是一个很重要的部分,特别是在b/s系统的构建过程中,数据库操作几乎成为了一个必不可少的操作。调用存储过程实现数据库操作使很多程序员使用的方法,而且大多数的程序员都是能使用存储过程就使用存储过程,很少直接使用sql语句,所以存储过程是很有用而且很重要的。

  存储过程简介

  简单的说,存储过程是由一些sql语句和控制语句组成的被封装起来的过程,它驻留在数据库中,可以被客户应用程序调用,也可以从另一个过程或触发器调用。它的参数可以被传递和返回。与应用程序中的函数过程类似,存储过程可以通过名字来调用,而且它们同样有输入参数和输出参数。

  根据返回值类型的不同,我们可以将存储过程分为三类:返回记录集的存储过程,返回数值的存储过程(也可以称为标量存储过程),以及行为存储过程。顾名思义,返回记录集的存储过程的执行结果是一个记录集,典型的例子是从数据库中检索出符合某一个或几个条件的记录;返回数值的存储过程执行完以后返回一个值,例如在数据库中执行一个有返回值的函数或命令;最后,行为存储过程仅仅是用来实现数据库的某个功能,而没有返回值,例如在数据库中的更新和删除操作。

  使用存储过程的好处

  相对于直接使用sql语句,在应用程序中直接调用存储过程有以下好处:

  (1)减少网络通信量。调用一个行数不多的存储过程与直接调用sql语句的网络通信量可能不会有很大的差别,可是如果存储过程包含上百行sql语句,那么其性能绝对比一条一条的调用sql语句要高得多。

  (2)执行速度更快。有两个原因:首先,在存储过程创建的时候,数据库已经对其进行了一次解析和优化。其次,存储过程一旦执行,在内存中就会保留一份这个存储过程,这样下次再执行同样的存储过程时,可以从内存中直接调用。

  (3)更强的适应性:由于存储过程对数据库的访问是通过存储过程来进行的,因此数据库开发人员可以在不改动存储过程接口的情况下对数据库进行任何改动,而这些改动不会对应用程序造成影响。

  (4) 布式工作:应用程序和数据库的编码工作可以分别独立进行,而不会相互压制。

  由以上的分析可以看到,在应用程序中使用存储过程是很有必要的。

  两种不同的存储过程调用方法

  为了突出新方法的优点,首先介绍一下在.net中调用存储过程的“官方”方法。另外,本文的所有示例程序均工作于sqlserver数据库上,其它情况类似,以后不再一一说明。本文所有例子均采用c#语言。

  要在应用程序中访问数据库,一般性的步骤是:首先声明一个数据库连接sqlconnection,然后声明一个数据库命令 sqlcommand,用来执行sql语句和存储过程。有了这两个对象后,就可以根据自己的需要采用不同的执行方式达到目的。需要补充的是,不要忘记在页面上添加如下的引用语句:using system.data.sqlclient。

  就执行存储过程来说,如果执行的是第一类存储过程,那么就要用一个dataadapter将结果填充到一个dataset中,然后就可以使用数据网格控件将结果呈现在页面上了;如果执行的是第二和第三种存储过程,则不需要此过程,只需要根据特定的返回判定操作是否成功完成即可。

  (1)执行一个没有参数的存储过程的代码如下:

sqlconnection conn=new sqlconnection(“connectionstring”);
sqldataadapter da = new sqldataadapter();
da.selectcommand = new sqlcommand();
da.selectcommand.connection = conn;
da.selectcommand.commandtext = “nameofprocedure”;
da.selectcommand.commandtype = commandtype.storedprocedure;

  然后只要选择适当的方式执行此处过程,用于不同的目的即可。

  (2)执行一个有参数的存储过程的代码如下(我们可以将调用存储过程的函数声明为exeprocedure(string inputdate)):

sqlconnection conn=new sqlconnection(“connectionstring”);
sqldataadapter da = new sqldataadapter();
da.selectcommand = new sqlcommand();
da.selectcommand.connection = conn;
da.selectcommand.commandtext = “nameofprocedure”;
da.selectcommand.commandtype = commandtype.storedprocedure;
(以上代码相同,以下为要添加的代码)
param = new sqlparameter(“@parametername”, sqldbtype.datetime);
param.direction = parameterdirection.input;
param.value = convert.todatetime(inputdate);
da.selectcommand.parameters.add(param);

  这样就添加了一个输入参数。若需要添加输出参数:

param = new sqlparameter(“@parametername”, sqldbtype.datetime);
param.direction = parameterdirection.output;
param.value = convert.todatetime(inputdate);
da.selectcommand.parameters.add(param);

  若要获得参储过程的返回值:

param = new sqlparameter(“@parametername”, sqldbtype.datetime);
param.direction = parameterdirection.returnvalue;
param.value = convert.todatetime(inputdate);
da.selectcommand.parameters.add(param);

  从上面的代码我们可以看出,当存储过程比较多或者存储过程的参数比较多时,这种方法会大大影响开发的速度;另外一方面,如果项目比较大,那么这些用于数据库逻辑的函数在以后的维护中也是一个很大的负担。那么,有没有一种改进的方法可以解决这个问题呢?想到在执行没有参数的存储过程时只需要传入一个存储过程的名字就可以调用相应的存储过程,而且在sqlserver数据库中我们可以直接在查询分析器中敲入“存储过程名(参数列表)”样的字符串就可以执行存储过程,那么,是否可以把这种思想应用到应用程序中呢?

  于是在编译器中键入相应代码。这些代码是在调用不带参数的存储过程的代码的基础上改的。具体代码如下:

sqlconnection conn=new sqlconnection(“connectionstring”);
sqldataadapter da = new sqldataadapter();
da.selectcommand = new sqlcommand();
da.selectcommand.connection = conn;
da.selectcommand.commandtext = “nameofprocedure(’para1’,’para2’,para3)”;
da.selectcommand.commandtype = commandtype.storedprocedure;

  为了使代码更具有代表性,要调用的存储过程的第一个和第二个参数都为字符串类型,第三个参数为整型。执行以后发现,完全可以达到预期的效果!

  两种调用方法的比较
  
  通过比较我们可以看到,第二种方法具有一个很明显的优点,那就是可以提高开发速度,节省开发时间,而且代码容易维护,在一定程度上也减少了系统大小。但是,由于对存储过程参数的处理比较笼统,如果要获取输出参数或者得到存储过程的返回值,这种方法就不能满足需要了。虽然如此,但是,这种方法毕竟可以让开发人员少些很大一部分的代码。如果不需要获取输出参数和返回值,那么几乎可以做到“一劳永逸”。因此在实际的程序开发中,这种方法还是具有一定的实用价值的。
标签:
发表评论 | Trackback

liyihongcug 发表于 2010-10-25 17:08

http://blog.myspace.cn/e/407426932.htm
http://www.dnbcw.com/biancheng/net/mcde191884.html
优化SQL Server的内存占用之执行缓存
作者:hnpvqve 来源: 浏览 271次 2010年3月12日【字号: 大 中 小 】【 打印 】【 收藏】
在论坛上常见有朋友抱怨,说SQL Server太吃内存了。这里笔者根据经验简单介绍一下内存相关的调优知识。首先说明一下SQL Server内存占用由哪几部分组成。SQL Server占用的内存主要由三部分组成:数据缓存(Data Buffer)、执行缓存(Procedure Cache)、以及SQL Server引擎程序。SQL Server引擎程序所占用缓存一般相对变化不大,则我们进行内存调优的主要着眼点在数据缓存和执行缓存的控制上。本文主要介绍一下执行缓存的调优。数据缓存的调优将在另外的文章中介绍。

  对于减少执行缓存的占用,主要可以通过使用参数化查询减少内存占用。
1、使用参数化查询减少执行缓存占用

  我们通过如下例子来说明一下使用参数化查询对缓存占用的影响。为方便试验,我们使用了一台没有其它负载的SQL Server进行如下实验。

  下面的脚本循环执行一个简单的查询,共执行10000次。

  首先,我们清空一下SQL Server已经占用的缓存:

  dbcc freeproccache

  然后,执行脚本:
  DECLARE @t datetime
  SET @t = getdate()
  SET NOCOUNT ON
  DECLARE @i INT, @count INT, @sql nvarchar(4000)
  SET @i = 20000
  WHILE @i <= 30000
  BEGIN
  SET @sql = 'SELECT @count=count(*) FROM P_Order WHERE MobileNo = ' + cast( @i as varchar(10) )
  EXEC sp_executesql @sql ,N'@count INT OUTPUT', @count OUTPUT
  SET @i = @i + 1
  END
  PRINT DATEDIFF( second, @t, current_timestamp )

  输出:

  DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。



  使用了11秒完成10000次查询。

  我们看一下SQL Server缓存中所占用的查询计划:

  Select Count(*) CNT,sum(size_in_bytes) TotalSize

  From sys.dm_exec_cached_plans

  查询结果:共有2628条执行计划缓存在SQL Server中。它们所占用的缓存达到:

  92172288字节 = 90012KB = 87 MB。

  我们也可以使用dbcc memorystatus 命令来检查SQL Server的执行缓存和数据缓存占用。

  执行结果如下:

  优化SQL Server的内存占用之执行缓存

  优化SQL Server的内存占用之执行缓存

  优化SQL Server的内存占用之执行缓存

  执行缓存占用了90088KB,有2629个查询计划在缓存里,有1489页空闲内存(每页8KB)可以被数据缓存和其他请求所使用。

  我们现在修改一下前面的脚本,然后重新执行一下dbcc freeproccache。再执行一遍修改后的脚本:

  DECLARE @t datetime
  SET @t = getdate()
  SET NOCOUNT ON
  DECLARE @i INT, @count INT, @sql nvarchar(4000)
  SET @i = 20000
  WHILE @i <= 30000
  BEGIN
   SET @sql = 'select @count=count(*) FROM P_Order WHERE MobileNo = @i'
  EXEC sp_executesql @sql, N'@count int output, @i int', @count OUTPUT, @i
  SET @i = @i + 1
  END
  PRINT DATEDIFF( second, @t, current_timestamp )

  输出:

  DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

  

  即这次只用1秒钟即完成了10000次查询。

  我们再看一下sys.dm_exec_cached_plans中的查询计划:
  Select Count(*) CNT,sum(size_in_bytes) TotalSize From sys.dm_exec_cached_plans

  查询结果:共有4条执行计划被缓存。它们共占用内存: 172032字节 = 168KB。

  如果执行dbcc memorystatus,则得到结果:

  优化SQL Server的内存占用之执行缓存

  优化SQL Server的内存占用之执行缓存

  优化SQL Server的内存占用之执行缓存

  有12875页空闲内存(每页8KB)可以被数据缓存所使用。

  到这里,我们已经看到了一个反差相当明显的结果。在现实中,这个例子中的前者,正是经常被使用的一种执行SQL脚本的方式(例如:在程序中通过合并字符串方式拼成一条SQL语句,然后通过ADO.NET或者ADO方式传入SQL Server执行)。

  解释一下原因:

  我们知道,SQL语句在执行前首先将被编译并通过查询优化引擎进行优化,从而得到优化后的执行计划,然后按照执行计划被执行。对于整体相似、仅仅是参数不同的SQL语句,SQL Server可以重用执行计划。但对于不同的SQL语句,SQL Server并不能重复使用以前的执行计划,而是需要重新编译出一个新的执行计划。同时,SQL Server在内存足够使用的情况下,此时并不主动清除以前保存的查询计划(注:对于长时间不再使用的查询计划,SQL Server也会定期清理)。这样,不同的SQL语句执行方式,就将会大大影响SQL Server中存储的查询计划数目。

  对此,我们一般可以通过两种方式实现参数化查询:一是尽可能使用存储过程执行SQL语句(这在现实中已经成为SQL Server DBA的一条原则),二是使用sp_executesql 方式执行单个SQL语句(注意不要像上面的第一个例子那样使用sp_executesql)。

  在现实的同一个软件系统中,大量的负载类型往往是类似的,所区别的也只是每次传入的具体参数值的不同。所以使用参数化查询是必要和可能的。另外,通过这个例子我们也看到,由于使用了参数化查询,不仅仅是优化了SQL Server内存占用,而且由于能够重复使用前面被编译的执行计划,使后面的执行不需要再次编译,最终执行10000次查询总共只使用了1秒钟时间。
2、检查并分析SQL Server执行缓存中的执行计划

  通过上面的介绍,我们可以看到SQL缓存所占用的内存大小。也知道了SQL Server执行缓存中的内容主要是各种SQL语句的执行计划。则要对缓存进行优化,就可以通过具体分析缓存中的执行计划,看看哪些是有用的、哪些是无用的执行计划来分析和定位问题。

  通过查询DMV: sys.dm_exec_cached_plans,可以了解数据库中的缓存情况,包括被使用的次数、缓存类型、占用的内存大小等。
  SELECT usecounts, cacheobjtype, objtype,size_in_bytes, plan_handle
  FROM sys.dm_exec_cached_plans

  优化SQL Server的内存占用之执行缓存

  通过缓存计划的plan_handle可以查询到该执行计划详细信息,包括所对应的SQL语句:
  SELECT TOP 100 usecounts,
  objtype,
  p.size_in_bytes,
  .
  FROM sys.dm_exec_cached_plans p
  OUTER APPLY sys.dm_exec_sql_text (p.plan_handle) sql
  ORDER BY usecounts

  优化SQL Server的内存占用之执行缓存

  我们可以选择针对那些执行计划占用较大内存、而被重用次数较少的SQL语句进行重点分析。看其调用方式是否合理。另外,也可以对执行计划被重复使用次数较多的SQL语句进行分析,看其执行计划是否已经经过优化。进一步,通过对查询计划的分析,还可以根据需要找到系统中最占用IO、CPU时间、执行次数最多的一些SQL语句,然后进行相应的调优分析。篇幅所限,这里不对此进行过多介绍。读者可以查阅联机丛书中的:sys.dm_exec_query_plan内容得到相关帮助。

  附:

  1:关于DBCC MEMORY, 可以查看微软的知识库: http://support.microsoft.com/kb/907877/EN-US

  2:关于sys.dm_exec_cached_plans和sys.dm_exec_sql_text,请参阅联机丛书。

innovate511 发表于 2010-10-25 23:49

感觉是SQL的广告贴呢,和DWBI没关系嘛:sleepy:
页: [1]
查看完整版本: 什么时候使用存储过程比较适合