发布新日志

  • https录制

    2012-05-02 13:08:37

    HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议

    它是一个安全通信通道,它基于HTTP开发,用于在客户计算机和服务器之间交换信息。它使用安全套接字层(SSL)进行信息交换,简单来说它是HTTP的安全版。

    在使用https协议不管是服务器端还是客户端都需要使用到ca证书来进行访问。

    一般来说,性能测试为所常见的为客户端使用https打开页面的方式。

    在这个情况下,首先是处理https访问过程中的证书

    1. 证书的准备

    常见的证书为:*.pfx格式,该种格式的证书可以通过双击运行安装到IE浏览器上。用户在访问的时候就可以使用到。

    但这种证书并不是LoadRunner所使用的类型,因此需要对其进行转换。将其转换为*.pem格式。

    转换方法如下:

     安装openssl后

     运行C:\<OpenSSL>/bin文件夹的openssl二进制文件,它将启动OpenSSL命令提示符

     执行以下命令:pkcs12 -in D:\test1.pfx -out D:\test01.pem –nodes

     执行后,将会在指定目录生成test01.pem文件,这个文件将会在下一个步骤,对LoadRunner进行配置的时候使用到。

    2.LR配置

    启动LoadRunner,打开Recording Option选项。

    留意红色框中的选项。

    选择后,单击New Enty

    红框中的配置为服务器的ip和端口号,按照测试所需要的实际地址进行配置就可以

    配置后,将Use specified client-side certificate[Base64/PEM]钩选,为使用客户端证书访问。

    单击...选择刚刚转换生成的客户端证书。

    如果你为证书有设置密码,在这里也需要输入。

    到此为止所有与http区别的配置就完成了。

    录制后,可以在脚本的开始,看到LoadRunner新生成的语句

    web_set_certificate_ex("CertFilePath=test01.pem",

    "CertFormat=PEM",

    "KeyFilePath=test01.pem",

    "KeyFormat=PEM",

    "Password=123456",

    "CertIndex=1",

    LAST);

    下面的操作和普通的页面性能测试一样。

    http://download.csdn.net/detail/mjh77_77/3451253

  • Weblogic.Tomcat.Apache.JBoss.Resin

    2012-03-12 13:56:52

     Weblogic.Tomcat.Apache.JBoss.Resin 都基于java的基础架构。

      Tomcat是一个初级的应用服务器,是一个免费的开放源代码的Web 应用服务器,含有Jsp和Servlet容器,但它也有不少缺点及不足:运行不够稳定,支持的并发连接数不够高,处理html时的效率不如apache,不能处理EJB等),目前最新版本是6.0.14。Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5 支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。 (小巧精悍,适用于小型网站,后台开发语言为java的开发者在开发时使用,Apache和Jboss联合应用可以满足企业级应用服务器的要求)

     

        JBoss含有Jsp和Servlet容器,也就可以做web容器,也包含EJB容器,是完整的J2EE应用服务器(优化版的tomcat);JBoss是一个运行EJB的J2EE应用服务器。它是开放源代码的项目,遵循最新的J2EE规范。从JBoss项目开始至今,它已经从一个EJB容器发展成为一个基于的J2EE的一个web操作系统(operating systemfor&nbsp;web),它体现了J2EE规范中最新的技术,含有Jsp和Servlet容器,也就可以做web容器,也包含EJB容器,是完整的J2EE应用服务器. 

     

        resin:resin是Caucho公司的产品。resin最突出的是它的性能非常高效,连html页面的效率也直追apache;支持servlet/jsp,EJB。虽然resin是开源的,不过有两种lisence:GPL和商用lisence,可以在GPL下免费使用,但注意商用的是要交钱的。(高效全功能的轻型服务器软件


      
      Weblogic是一个企业级的应用服务器,其中包括j2ee中的各类应用如jsp,servlet,ejb等,Weblogic是BEA公司的产品,是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器,将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。(技术上没得说,不过是商用服务器软件,适合大型商业应用)。

      Websphere是IBM公司的产品,基于J2EE的,对Jsp,Servlet,ejb的支持非常好!不过挺复杂的,随需应变的电子商务时代的最主要的软件平台。它使您的公司可以开发、部署和整合新一代的电子商务应用,如B2B 电子商务,并支持从简单的网页内容发布到企业级事务处理的商业应用,含有Jsp和Servlet容器,也就可以做web容器,也包含EJB容器,是完整的J2EE应用服务器。(如果有IBM其他配套产品,大型商用考虑使用
       

       Apache只是一个web服务器(web   server),解释静态网页Html,它的运行超级稳定;它的效率非常之高……。可能apache仅有的不足是,它本身仅提供html静态页面的功能,不能支持jsp、java servlet、asp等功能,但通过同其他应用服务器一起工作或添加插件来支持,例如和tomact联合应用。 Apache是世界使用排名第一的Web服务器。它可以运行在几乎所有广泛使用的计算机平台上。Apache源于NCSAhttpd服务器,经过多次修改,成为世界上最流行的Web服务器软件之一。Apache取自“a patchy server”的读音,意思是充满补丁的服务器,因为它是自由软件,所以不断有人来为它开发新的功能、新的特性、修改原来的缺陷。Apache的特点是简单、速度快、性能稳定,并可做代理服务器来使用。本来它只用于小型或试验Internet网络,后来逐步扩充到各种Unix系统中,尤其对Linux的支持相当完美。Apache有多种产品,可以支持SSL技术,支持多个虚拟主机。Apache是以进程为基础的结构,进程要比线程消耗更多的系统开支,不太适合于多处理器环境,因此,在一个Apache Web站点扩容时,通常是增加服务器或扩充群集节点而不是增加处理器。到目前为止Apache仍然是世界上用的最多的Web服务器,市场占有率达60%左右。世界上很多著名的网站如Amazon.com、Yahoo!、W3 Consortium、Financial Times等都是Apache的产物,它的成功之处主要在于它的源代码开放、有一支开放的开发队伍、支持跨平台的应用(可以运行在几乎所有的Unix、Windows、Linux系统平台上)以及它的可移植性等方面(稳定高效,通常需要与其他服务器软件或插件协同工作)。

      IIS是微软的Internet Information Server的简称(web   server)!主要是用来提供Web服务的,当然是针对它自己的产品asp的。首先,Internet Information Server的缩写为(IIS)是一个World Wide Web server。Gopher server和FTP server全部包容在里面。 IIS意味着你能发布网页,并且有ASP(Active Server Pages)、JAVA、VBscript产生页面,有着一些扩展功能。IIS支持一些有趣的东西,象有编辑环境的界面(FRONTPAGE)、有全文检索功能的(INDEX SERVER)、有多媒体功能的(NET SHOW);其次,IIS是随Windows NT Server 4.0一起提供的文件和应用程序服务器,是在Windows NT Server上建立Internet服务器的基本组件。它与Windows NT Server完全集成,允许使用Windows NT Server内置的安全性以及NTFS文件系统建立强大灵活的Internet/Intranet站点。IIS(Internet Information Services)是microsoft公司的服务器软件。IIS与上面介绍的服务器软件完全不同,它是在windows server(2000/2003/2008……)平台上免费附加的,买了windows就可以免费使用IIS了,开源就别想了;与windows平台紧密结合在一起,支持html和asp,aspx等。既然是微软的产品,自然只能在windows平台上,……。(如果是只用windows系统,想支持asp的就用它吧

  • Apache Http Server和Tomcat 之区别

    2012-03-06 17:18:38

    Apache官方网站:http://www.apache.org/ 
    Tomcat官方网站:http://tomcat.apache.org/

    1. Apache是web服务器,Tomcat是应用(java)服务器,它只是一个servlet容器,是Apache的扩展。

    2. Apache和Tomcat都可以做为独立的web服务器来运行,但是Apache不能解释java程序(jsp,serverlet)。

    3. Apache是普通服务器,本身只支持html即普通网页。不过可以通过插件支持php,还可以与Tomcat连通(单向Apache连接Tomcat,就是说通过Apache可以访问Tomcat资源。反之不然)

    4. 两者都是一种容器,只不过发布的东西不同:Apache是html容器,功能像IIS一样;Tomcat是jsp/servlet容器,用于发布jsp及java的,类似的有IBM的webshere、EBA的Weblogic,sun的JRun等等。

    5. Apache和Tomcat是独立的,在通一台服务器上可以集成。

    打个比方:Apache是一辆卡车,上面可以装一些东西如html等。但是不能装水,要装水必须要有容器(桶),Tomcat就是一个桶(装像Java这样的水),而这个桶也可以不放在卡车上。

    Apache只支持静态网页,但像asp,php,cgi,jsp等动态网页就需要Tomcat来处理。

    Apache和Tomcat整合使用:如果客户端请求的是静态页面,则只需要Apache服务器响应请求;如果客户端请求动态页面,则是Tomcat服务器响应请求;因为jsp是服务器端解释代码的,这样整合就可以减少Tomcat的服务开销 。

    Apache是世界使用排名第一的Web服务器。它可以运行在几乎所有广泛使用的计算机平台上。

    Apache 源于 NCSAhttpd服务器,经过多次修改,成为世界上最流行的Web服务器软件之一。Apache取自“a patchy server”的读音,意思是充满补丁的服务器,因为它是自由软件,所以不断有人来为它开发新的功能、新的特性、修改原来的缺陷。Apache的特点是简 单、速度快、性能稳定,并可做代理服务器来使用。Apache对Linux的支持相当完美。

    Apache有多种产品,可以支持SSL技术,支持多个虚拟主机。Apache是以进程为基础的结构,进程要比线程消 耗更多的系统开支,不太适合于多处理器环境,因此,在一个Apache Web站点扩容时,通常是增加服务器或扩充群集节点而不是增加处理器。到目前为止Apache仍然是世界上用的最多的Web服务器,市场占有率达60%左 右。世界上很多著名的网站如Amazon.com、Yahoo!、W3 Consortium、Financial Times等都是Apache的产物,它的成功之处主要在于它的源代码开放、有一支开放的开发队伍、支持跨平台的应用(可以运行在几乎所有的Unix、 Windows、Linux系统平台上)以及它的可移植性等方面。

    Apache的诞生极富有戏剧性。当NCSA WWW服务器项目停顿后,那些使用NCSA WWW服务器的人们开始交换他们用于该服务器的补丁程序,他们也很快认识到成立管理这些补丁程序的论坛是必要的。就这样,诞生了Apache Group,后来这个团体在NCSA的基础上创建了Apache。如果你准备选择Web服务器,毫无疑问Apache是你的最佳选择。

    Tomcat是一个开放源代码、运行servlet和JSP Web应用软件的基于Java的Web应用软件容器。Tomcat Server是根据servlet和JSP规范进行执行的,因此我们就可以说Tomcat Server也实行了Apache-Jakarta规范且比绝大多数商业应用软件服务器要好。

    Tomcat是Java Servlet 2.2和JavaServer Pages 1.1技术的标准实现,是基于Apache许可证下开发的自由软件。Tomcat是完全重写的Servlet API 2.2和JSP 1.1兼容的Servlet/JSP容器。Tomcat使用了JServ的一些代码,特别是Apache服务适配器。随着Catalina Servlet引擎的出现,Tomcat第四版号的性能得到提升,使得它成为一个值得考虑的Servlet/JSP容器,因此目前许多WEB服务器都是采 用Tomcat。

    **JSP=Java Server Pages (Java服务器网页) 
    **ASP=Active Server Pages (活动服务器网页) 
    **CGI=Common Gateway Interface (通用网关接口) 
    **J2EE=Java 2 Platform, Enterprise Edition (Java2平台,企业版) 
    **XML=eXtensible Markup Language (扩展标记语言) 
    **IIS=Internet Information Server (互联网信息服务器)

    ----------------- 
    关于IIS: 
    IIS是Windows系统提供的一种服务,它包括WWW服务器、FTP服务器和SMTP服务器。 
    在UNIX 或Linux平台上,Apache就是网站服务器。而对于Windows NT/2000来说,IIS就是标准的网站服务器。IIS是一种服务,是Windows 2000 Server系列的一个组件。不同于一般的应用程序,它就像驱动程序一样是操作系统的一部分,具有在系统启动时被同时启动的服务功能。

  • GET与POST方法的区别

    2012-03-06 15:48:21

    一直没有仔细的研究到底GET方法和POST方法之间有什么区别,直到今天做一个文件上传的表单。文件上传的表单必须使用post方法提交,为什么必须这样?Get方法为什么不行?

              带着这样的问题我查了一下《计算机网络》这本书对POST和GET方法在HTTP协议中的简单解释:HTTP协议定义了通信的两种报文:请求报文和响应报文。对于请求报文来说,它的通用格式包含三个主要部分:请求行、首部行、实体主体。通常请求行的格式为:方法字段 、 URL字段、 HTTP协议版本字段,其中方法字段就包括GET、POST方法等。

    当方法字段是GET方法时内容主体为空,而是用POST方法时才使用实体主体。

         HTTP客户机通常在用户提交表单时使用POST方法,这时,实体主体中所包含的就是用户在表单中的输入值。当然,GET方法也可以提交表单中的数据,它是通过将用户在表单中的输入值传送到正确的URL来实现的。这就是我们通常所看到的http://ei.hust.edu.cn?username="libis"&age=12

    可以看出,在表单提交的方式上两者存在很大的不同:
    Post方法将表单数据填充到HTTP请求报文中的实体主体部分,使用者是看不到的。而GET方法则是将表单数据直接传送到请求行的URL字段来实现表单数据提交的。这也就决定了使用POST是相对比较安全的,而是用GET方法的安全性就很低。

    除此之外,GET方法和POST方法另一个重要的区别是:
    GET方法传送的数据量较小,一般只有2k。而POST方法传送的数据量较大,理论上没有上限。


     
    最近读者:
  • eve大小

    2010-10-08 16:32:02

    1. 发现是.eve文件过大导致的。那怎么把.eve文件弄小些呢?
       2. 修改Controller里面的Diagnostics。有选择的把不需要的项Disable掉就可以了。目前破解版本只能有Web Page Diagnostics生效了,也就是只能把这个项Disable掉。
              * 说明: Web Page Diagnostics是会把整个网页每个图片,事务的细节都记录到.eve文件中,如每个图片的响应时间等。
              * 附带的好处是,负载机的负担变小了很多。
              * 建议:当不需要Diagnostics时把它Disable掉,这样可以提高负载机的性能,减小结果文件的大小。
       3. 修改Controller监控设置的取样间隔时间,默认是3秒。按需要设置成10~15或更长!(重启Controller生效)
  • C#中引用VC dll方法

    2009-11-30 15:07:28

    1.       直接引用VC DLLc#中是不行的,vc dll  是非托管的,不能直接引用

    通过C#中的DllImport直接调用vc dll

    http://www.cnblogs.com/xumingming/archive/2008/10/10/1308248.html

    2.    在用DllImport时类型转换特别麻烦

    微软也意识到这个比较头痛,于是出了个小工具P/Invoke Interop Assistant,可以自动转换

    http://blog.csdn.net/Donjuan/archive/2009/02/05/3865026.aspx

     

    http://www.cnblogs.com/emanlee/archive/2008/10/16/1312984.html

     

  • Webservices性能测试

    2009-09-22 14:55:43

    Web Service:是一种革命性的分布是计算技术。它使用基于XML的消息处理作为基本的数据通讯方式,消除使用不同组件模型、操作系统和编程语言的系统之间存在的差异,使异类系统能够作为计算网络的一部分协同运行。开发人员可以使用像过去创建分布式应用程序时使用组件的方式,创建由各种来源的Web服务组合在一起的应用程序。由于Web服务是建立在一些通用协议的基础上,如HTTP(Hypertext Transfer Protocol, WWW服务程序所用的协议),SOAP(Simple Object Access Protocol,简单对象访问协议),XML,WSDL(Web Services Description Language,Web服务描述语言),UDDI(Universal Description,Discovery,and Integration,通用描述发现和集成协议)等,这些协议在涉及到操作系统、对象模型和编程语言的选择时,没有任何倾向,因此Web服务将会有很强的生命力。

     

    webservice的调用过程

        客户端:取得服务端的服务描述文件WSDL,解析该文件的内容,了解服务端的服务信息,以及调用方式。根据需要,生成恰当的SOAP请求消息(指定调用的方法,已经调用的参数),发往服务端。等待服务端返回的SOAP回应消息,解析得到返回值。

    服务端:生成服务描述文件,以供客户端获取。接收客户端发来的SOAP请求消息,解析其中的方法调用和参数格式。根据WSDL和WSML的描述,调用相应的COM对象来完成指定功能,并把返回值放入SOAP回应消息返回给用户。

     

     

  • 杀进程

    2009-06-25 16:46:04

    @echo on
    for /f %%i in (host.txt) do taskkill /s %%i /u user /p password /f /im mdrv.exe
    pause

    当然这是做成批处理文件,在进行大量测试时,批处理文件用处相当大!
    在做容量测试时,停掉controller这个相当快捷!

    以下是系统对TASKKILL帮助说明:
    TASKKILL [/S system [/U username [/P [password]]]]
             { [/FI filter] [/PID processid | /IM imagename] } [/F] [/T]

    描述:
        这个命令行工具可用来结束至少一个进程。
        可以根据进程 id 或图像名来结束进程。

    参数列表:
        /S    system           指定要连接到的远程系统。

        /U    [domain\]user    指定应该在哪个用户上下文
                               执行这个命令。

        /P    [password]       为提供的用户上下文指定
                               密码。如果忽略,提示输入。

        /F                     指定要强行终止
                               进程。

        /FI   filter           指定筛选进或筛选出查询的
                               的任务。

        /PID  process id       指定要终止的进程的
                               PID。

        /IM   image name       指定要终止的进程的
                               图像名。通配符 '*'
                               可用来指定所有图像名。

        /T                     Tree kill: 终止指定的进程
                               和任何由此启动的子进程。

        /?                     显示帮助/用法。

    筛选器:
        筛选器名      有效运算符                有效值
        -----------   ---------------           --------------
        STATUS        eq, ne                    运行 | 没有响应
        IMAGENAME     eq, ne                    图像名
        PID           eq, ne, gt, lt, ge, le    PID 值
        SESSION       eq, ne, gt, lt, ge, le    会话编号
        CPUTIME       eq, ne, gt, lt, ge, le    CPU 时间,格式为
                                                hh:mm:ss。
                                                hh - 时,
                                                mm - 钟,ss - 秒
        MEMUSAGE      eq, ne, gt, lt, ge, le    内存使用,单位为 KB
        USERNAME      eq, ne                    用户名,格式为
                                                [domain\]user
        MODULES       eq, ne                    DLL 名
        SERVICES        eq, ne                    服务名
        WINDOWTITLE     eq, ne                    窗口标题

    注意: 只有带有筛选器的情况下,才能跟 /IM 切换使用通配符 '*'。

    注意: 远程进程总是要强行终止,
          不管是否指定了 /F 选项。

    例如:
        TASKKILL /S system /F /IM notepad.exe /T
        TASKKILL /PID 1230 /PID 1241 /PID 1253 /T
        TASKKILL /F /IM notepad.exe /IM mspaint.exe
        TASKKILL /F /FI "PID ge 1000" /FI "WINDOWTITLE ne untitle*"
        TASKKILL /F /FI "USERNAME eq NT AUTHORITY\SYSTEM" /IM notepad.exe
        TASKKILL /S system /U domain\username /FI "USERNAME ne NT*" /IM *
        TASKKILL /S system /U username /P password /FI "IMAGENAME eq note*"
  • 测试种类

    2009-06-23 14:28:22

    回归测试:是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。自动回归测试将大幅降低系统测试、维护升级等阶段的成本。回归测试包括两部分:函数本身的测试、其他代码的测试(版本更新时一般用到回归测试)
     
    单元测试:是在软件开发过程中要进行的最低级别的测试活动,在单元测试活动中,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。(一个模块一个模块的测试)

    集成测试,也叫组装测试或联合测试。在单元测试的基础上,将所有模块按照设计要求,组装成为子系统或系统,进行集成测试。实践表明,一些模块虽然能够单独地工作,但并不能保证连接起来也能正常的工作。程序在某些局部反映不出来的问题,在全局上很可能暴露出来,影响功能的实现。(把模块一个个串起来测试)

    系统测试是将经过测试的子系统装配成一个完整系统来测试。它是检验系统是否确实能提供系统方案说明书中指定功能的有效方法。
     
    5.集成测试通常都有那些策略?
    1、在把各个模块连接起来的时候,穿越模块接口的数据是否会丢失;
    2、各个子功能组合起来,能否达到预期要求的父功能;
    3、一个模块的功能是否会对另一个模块的功能产生不利的影响;
    4、全局数据结构是否有问题;
    5、单个模块的误差积累起来,是否会放大,从而达到不可接受的程度。
     
    7.一个缺陷测试报告的组成
    缺陷的标题,缺陷的基本信息,复现缺陷的操作步骤,缺陷的实际结果描述,期望的正确结果描述,注释文字和截取的缺陷图象。
     
    软件本地化测试比功能测试都有哪些方面需要注意?
    软件本地化测试的目的:
    软件本地化测试的测试策略:1.本地化软件要在各种本地化操作系统上安装并测试。2.源语言软件安装在另一台相同源语言操作系统上,作为对比测试。3.重点测试因本地化引起的软件的功能和软件界面的错误。4.测试本地化软件的翻译质量。5.手工测试和自动测试相结合。
  • 触发器

    2009-06-23 10:38:52

    触发器是一种特殊类型的存储过程,不由用户直接调用。创建触发器时会对其进行定义,以便在对特定表或列作特定类型的数据修改时执行。
      CREATE PROCEDURE 或 CREATE TRIGGER 语句不能跨越批处理。即存储过程或触发器始终只能在一个批处理中创建并编译到一个执行计划中。
      用触发器还可以强制执行业务规则
      Microsoft&reg; SQL Server™ 2000 提供了两种主要机制来强制业务规则和数据完整性:约束和触发器。触发器是一种特殊类型的存储过程,它在指定的表中的数据发生变化时自动生效。唤醒调用触发器以响应 INSERT、UPDATE 或 DELETE 语句。触发器可以查询其它表,并可以包含复杂的 Transact-SQL 语句。将触发器和触发它的语句作为可在触发器内回滚的单个事务对待。如果检测到严重错误(例如,磁盘空间不足),则整个事务即自动回滚。
      触发器的优点如下: 
      触发器可通过数据库中的相关表实现级联更改;不过,通过级联引用完整性约束可以更有效地执行这些更改。
      触发器可以强制比用 CHECK 约束定义的约束更为复杂的约束。 
      与 CHECK 约束不同,触发器可以引用其它表中的列。例如,触发器可以使用另一个表中的 SELECT 比较插入或更新的数据,以及执行其它操作,如修改数据或显示用户定义错误信息。
      触发器也可以评估数据修改前后的表状态,并根据其差异采取对策。
      一个表中的多个同类触发器(INSERT、UPDATE 或 DELETE)允许采取多个不同的对策以响应同一个修改语句。 
      比较触发器与约束
      约束和触发器在特殊情况下各有优势。触发器的主要好处在于它们可以包含使用 Transact-SQL 代码的复杂处理逻辑。因此,触发器可以支持约束的所有功能;但它在所给出的功能上并不总是最好的方法。
      实体完整性总应在最低级别上通过索引进行强制,这些索引或是 PRIMARY KEY 和 UNIQUE 约束的一部分,或是在约束之外独立创建的。假设功能可以满足应用程序的功能需求,域完整性应通过 CHECK 约束进行强制,而引用完整性 (RI) 则应通过 FOREIGN KEY 约束进行强制。
      在约束所支持的功能无法满足应用程序的功能要求时,触发器就极为有用。例如: 
      除非 REFERENCES 子句定义了级联引用操作,否则 FOREIGN KEY 约束只能以与另一列中的值完全匹配的值来验证列值。
      CHECK 约束只能根据逻辑表达式或同一表中的另一列来验证列值。如果应用程序要求根据另一个表中的列验证列值,则必须使用触发器。
      约束只能通过标准的系统错误信息传递错误信息。如果应用程序要求使用(或能从中获益)自定义信息和较为复杂的错误处理,则必须使用触发器。 
      触发器可通过数据库中的相关表实现级联更改;不过,通过级联引用完整性约束可以更有效地执行这些更改。 
      触发器可以禁止或回滚违反引用完整性的更改,从而取消所尝试的数据修改。当更改外键且新值与主键不匹配时,此类触发器就可能发生作用。例如,可以在 titleauthor.title_id 上创建一个插入触发器,使它在新值与 titles.title_id 中的某个值不匹配时回滚一个插入。不过,通常使用 FOREIGN KEY 来达到这个目的。
      如果触发器表上存在约束,则在 INSTEAD OF 触发器执行后但在 AFTER 触发器执行前检查这些约束。如果约束破坏,则回滚 INSTEAD OF 触发器操作并且不执行 AFTER 触发器。
      SQL触发器语法
      语法
      CREATE TRIGGER trigger_name
      ON { table | view }
      [ WITH ENCRYPTION ]
      {
      { { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ DELETE ] [ UPDATE ] }
      [ WITH APPEND ]
      [ NOT FOR REPLICATION ]
      AS
      [ { IF UPDATE ( column )
      [ { AND | OR } UPDATE ( column ) ]
      [ ...n ]
      | IF ( COLUMNS_UPDATED ( ) updated_bitmask )
      column_bitmask [ ...n ]
      } ]
      sql_statement [ ...n ]
      }
      }
      参数
      trigger_name
      是触发器的名称。触发器名称必须符合标识符规则,并且在数据库中必须唯一。可以选择是否指定触发器所有者名称。
      Table | view
      是在其上执行触发器的表或视图,有时称为触发器表或触发器视图。可以选择是否指定表或视图的所有者名称。
      WITH ENCRYPTION
      加密 syscomments 表中包含 CREATE TRIGGER 语句文本的条目。使用 WITH ENCRYPTION 可防止将触发器作为 SQL Server 复制的一部分发布。
      AFTER
      指定触发器只有在触发 SQL 语句中指定的所有操作都已成功执行后才激发。所有的引用级联操作和约束检查也必须成功完成后,才能执行此触发器。
      如果仅指定 FOR 关键字,则 AFTER 是默认设置。
      不能在视图上定义 AFTER 触发器。
      INSTEAD OF
      指定执行触发器而不是执行触发 SQL 语句,从而替代触发语句的操作。
      在表或视图上,每个 INSERT、UPDATE 或 DELETE 语句最多可以定义一个 INSTEAD OF 触发器。然而,可以在每个具有 INSTEAD OF 触发器的视图上定义视图。
      INSTEAD OF 触发器不能在 WITH CHECK OPTION 的可更新视图上定义。如果向指定了 WITH CHECK OPTION 选项的可更新视图添加 INSTEAD OF 触发器,SQL Server 将产生一个错误。用户必须用 ALTER VIEW 删除该选项后才能定义 INSTEAD OF 触发器。
      { [DELETE] [,] [INSERT] [,] [UPDATE] }
      是指定在表或视图上执行哪些数据修改语句时将激活触发器的关键字。必须至少指定一个选项。在触发器定义中允许使用以任意顺序组合的这些关键字。如果指定的选项多于一个,需用逗号分隔这些选项。
      对于 INSTEAD OF 触发器,不允许在具有 ON DELETE 级联操作引用关系的表上使用 DELETE 选项。同样,也不允许在具有 ON UPDATE 级联操作引用关系的表上使用 UPDATE 选项。
      WITH APPEND
      指定应该添加现有类型的其它触发器。只有当兼容级别是 65 或更低时,才需要使用该可选子句。如果兼容级别是 70 或更高,则不必使用 WITH APPEND 子句添加现有类型的其它触发器(这是兼容级别设置为 70 或更高的 CREATE TRIGGER 的默认行为)。有关更多信息,请参见 sp_dbcmptlevel。
      WITH APPEND 不能与 INSTEAD OF 触发器一起使用,或者,如果显式声明 AFTER 触发器,也不能使用该子句。只有当出于向后兼容而指定 FOR 时(没有 INSTEAD OF 或 AFTER),才能使用 WITH APPEND。以后的版本将不支持 WITH APPEND 和 FOR(将被解释为 AFTER)。
      NOT FOR REPLICATION
      表示当复制进程更改触发器所涉及的表时,不应执行该触发器。
      AS
      是触发器要执行的操作。
      sql_statement
      是触发器的条件和操作。触发器条件指定其它准则,以确定 DELETE、INSERT 或 UPDATE 语句是否导致执行触发器操作。
      当尝试 DELETE、INSERT 或 UPDATE 操作时,Transact-SQL语句中指定的触发器操作将生效。
      触发器可以包含任意数量和种类的 Transact-SQL 语句。触发器旨在根据数据修改语句检查或更改数据;它不应将数据返回给用户。触发器中的 Transact-SQL 语句常常包含控制流语言。CREATE TRIGGER 语句中使用几个特殊的表:
      * deleted 和 inserted 是逻辑(概念)表。这些表在结构上类似于定义触发器的表(也就是在其中尝试用户操作的表);这些表用于保存用户操作可能更改的行的旧值或新值。例如,若要检索 deleted 表中的所有值,请使用:
      SELECT *
      FROM deleted
      * 如果兼容级别等于 70,那么在 DELETE、INSERT 或 UPDATE 触发器中,SQL Server 将不允许引用 inserted 和 deleted 表中的 text、ntext 或 image 列。不能访问 inserted 和 deleted 表中的 text、ntext 和 image 值。若要在 INSERT 或 UPDATE 触发器中检索新值,请将 inserted 表与原始更新表联接。当兼容级别是 65 或更低时,对 inserted 或 deleted 表中允许空值的text、ntext 或 image 列,将返回空值;如果这些列不可为空,则返回零长度字符串。
      当兼容级别是 80 或更高时,SQL Server 允许在表或视图上通过 INSTEAD OF 触发器更新 text、ntext 或 image 列。
      n
      是表示触发器中可以包含多条 Transact-SQL 语句的占位符。对于 IF UPDATE (column) 语句,可以通过重复 UPDATE (column) 子句包含多列。
      IF UPDATE (column)
      测试在指定的列上进行的 INSERT 或 UPDATE 操作,不能用于 DELETE 操作。可以指定多列。因为在 ON 子句中指定了表名,所以在 IF UPDATE 子句中的列名前不要包含表名。若要测试在多个列上进行的 INSERT 或 UPDATE 操作,请在第一个操作后指定单独的 UPDATE(column) 子句。在 INSERT 操作中 IF UPDATE 将返回 TRUE 值,因为这些列插入了显式值或隐性 (NULL) 值。
      说明 IF UPDATE (column) 子句的功能等同于 IF、IF...ELSE 或 WHILE 语句,并且可以使用 BEGIN...END 语句块。有关更多信息,请参见控制流语言。
      可以在触发器主体中的任意位置使用 UPDATE (column)。
      column
      是要测试 INSERT 或 UPDATE 操作的列名。该列可以是 SQL Server 支持的任何数据类型。但是,计算列不能用于该环境中。有关更多信息,请参见数据类型。
      IF (COLUMNS_UPDATED())
      测试是否插入或更新了提及的列,仅用于 INSERT 或 UPDATE 触发器中。COLUMNS_UPDATED 返回 varbinary 位模式,表示插入或更新了表中的哪些列。
      COLUMNS_UPDATED 函数以从左到右的顺序返回位,最左边的为最不重要的位。最左边的位表示表中的第一列;向右的下一位表示第二列,依此类推。如果在表上创建的触发器包含 8 列以上,则 COLUMNS_UPDATED 返回多个字节,最左边的为最不重要的字节。在 INSERT 操作中 COLUMNS_UPDATED 将对所有列返回 TRUE 值,因为这些列插入了显式值或隐性 (NULL) 值。
      可以在触发器主体中的任意位置使用 COLUMNS_UPDATED。
      bitwise_operator
      是用于比较运算的位运算符。
      updated_bitmask
      是整型位掩码,表示实际更新或插入的列。例如,表 t1 包含列 C1、C2、C3、C4 和 C5。假定表 t1 上有 UPDATE 触发器,若要检查列 C2、C3 和 C4 是否都有更新,指定值 14;若要检查是否只有列 C2 有更新,指定值 2。
      comparison_operator
      是比较运算符。使用等号 (=) 检查 updated_bitmask 中指定的所有列是否都实际进行了更新。使用大于号 (>) 检查 updated_bitmask 中指定的任一列或某些列是否已更新。
      column_bitmask
      是要检查的列的整型位掩码,用来检查是否已更新或插入了这些列。
      注释
      触发器常常用于强制业务规则和数据完整性。SQL Server 通过表创建语句(ALTER TABLE 和 CREATE TABLE)提供声明引用完整性 (DRI);但是 DRI 不提供数据库间的引用完整性。若要强制引用完整性(有关表的主键和外键之间关系的规则),请使用主键和外键约束(ALTER TABLE 和 CREATE TABLE 的 PRIMARY KEY 和 FOREIGN KEY 关键字)。如果触发器表存在约束,则在 INSTEAD OF 触发器执行之后和 AFTER 触发器执行之前检查这些约束。如果违反了约束,则回滚 INSTEAD OF 触发器操作且不执行(激发)AFTER 触发器。
      可用 sp_settriggerorder 指定表上第一个和最后一个执行的 AFTER 触发器。在表上只能为每个 INSERT、UPDATE 和 DELETE 操作指定一个第一个执行和一个最后一个执行的 AFTER 触发器。如果同一表上还有其它 AFTER 触发器,则这些触发器将以随机顺序执行。
      如果 ALTER TRIGGER 语句更改了第一个或最后一个触发器,则将除去已修改触发器上设置的第一个或最后一个特性,而且必须用 sp_settriggerorder 重置排序值。
      只有当触发 SQL 语句(包括所有与更新或删除的对象关联的引用级联操作和约束检查)成功执行后,AFTER 触发器才会执行。AFTER 触发器检查触发语句的运行效果,以及所有由触发语句引起的 UPDATE 和 DELETE 引用级联操作的效果。
      触发器限制
      CREATE TRIGGER 必须是批处理中的第一条语句,并且只能应用到一个表中。
      触发器只能在当前的数据库中创建,不过触发器可以引用当前数据库的外部对象。
      如果指定触发器所有者名称以限定触发器,请以相同的方式限定表名。
      在同一条 CREATE TRIGGER 语句中,可以为多种用户操作(如 INSERT 和 UPDATE)定义相同的触发器操作。
      如果一个表的外键在 DELETE/UPDATE 操作上定义了级联,则不能在该表上定义 INSTEAD OF DELETE/UPDATE 触发器。
      在触发器内可以指定任意的 SET 语句。所选择的 SET 选项在触发器执行期间有效,并在触发器执行完后恢复到以前的设置。
      与使用存储过程一样,当触发器激发时,将向调用应用程序返回结果。若要避免由于触发器激发而向应用程序返回结果,请不要包含返回结果的 SELECT 语句,也不要包含在触发器中进行变量赋值的语句。包含向用户返回结果的 SELECT 语句或进行变量赋值的语句的触发器需要特殊处理;这些返回的结果必须写入允许修改触发器表的每个应用程序中。如果必须在触发器中进行变量赋值,则应该在触发器的开头使用 SET NOCOUNT 语句以避免返回任何结果集。
      DELETE 触发器不能捕获 TRUNCATE TABLE 语句。尽管 TRUNCATE TABLE 语句实际上是没有 WHERE 子句的 DELETE(它删除所有行),但它是无日志记录的,因而不能执行触发器。因为 TRUNCATE TABLE 语句的权限默认授予表所有者且不可转让,所以只有表所有者才需要考虑无意中用 TRUNCATE TABLE 语句规避 DELETE 触发器的问题。
      无论有日志记录还是无日志记录,WRITETEXT 语句都不激活触发器。
      触发器中不允许以下 Transact-SQL 语句:
      ALTER DATABASE CREATE DATABASE DISK INIT
      DISK RESIZE DROP DATABASE LOAD DATABASE
      LOAD LOG RECONFIGURE RESTORE DATABASE
      RESTORE LOG 
      说明 由于 SQL Server 不支持系统表中的用户定义触发器,因此建议不要在系统表中创建用户定义触发器。
      多个触发器
      SQL Server 允许为每个数据修改事件(DELETE、INSERT 或 UPDATE)创建多个触发器。例如,如果对已有 UPDATE 触发器的表执行 CREATE TRIGGER FOR UPDATE,则将创建另一个更新触发器。在早期版本中,在每个表上,每个数据修改事件(INSERT、UPDATE 或 DELETE)只允许有一个触发器。
      说明 如果触发器名称不同,则 CREATE TRIGGER(兼容级别为 70)的默认行为是在现有的触发器中添加其它触发器。如果触发器名称相同,则 SQL Server 返回一条错误信息。但是,如果兼容级别等于或小于 65,则使用 CREATE TRIGGER 语句创建的新触发器将替换同一类型的任何现有触发器,即使触发器名称不同。有关更多信息,请参见 sp_dbcmptlevel。
      递归触发器
      当在 sp_dboption 中启用 recursive triggers 设置时,SQL Server 还允许触发器的递归调用。
      递归触发器允许发生两种类型的递归:
      * 间接递归
      * 直接递归 
      使用间接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T2。在这种情况下,触发器 T2 将激发并更新 T1。
      使用直接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T1。由于表 T1 被更新,触发器 TR1 再次激发,依此类推。
      下例既使用了间接触发器递归,又使用了直接触发器递归。假定在表 T1 中定义了两个更新触发器 TR1 和 TR2。触发器 TR1 递归地更新表 T1。UPDATE 语句使 TR1 和 TR2 各执行一次。而 TR1 的执行将触发 TR1(递归)和 TR2 的执行。给定触发器的 inserted 和 deleted 表只包含与唤醒调用触发器的 UPDATE 语句相对应的行。
      说明 只有启用 sp_dboption 的 recursive triggers 设置,才会发生上述行为。对于为给定事件定义的多个触发器,并没有确定的执行顺序。每个触发器都应是自包含的。
      禁用 recursive triggers 设置只能禁止直接递归。若要也禁用间接递归,请使用 sp_configure 将 nested triggers 服务器选项设置为 0。
      如果任一触发器执行了 ROLLBACK TRANSACTION 语句,则无论嵌套级是多少,都不会进一步执行其它触发器。
      嵌套触发器
      触发器最多可以嵌套 32 层。如果一个触发器更改了包含另一个触发器的表,则第二个触发器将激活,然后该触发器可以再调用第三个触发器,依此类推。如果链中任意一个触发器引发了无限循环,则会超出嵌套级限制,从而导致取消触发器。若要禁用嵌套触发器,请用 sp_configure 将 nested triggers 选项设置为 0(关闭)。默认配置允许嵌套触发器。如果嵌套触发器是关闭的,则也将禁用递归触发器,与 sp_dboption 的 recursive triggers 设置无关。
      延迟名称解析
      SQL Server 允许 Transact-SQL 存储过程、触发器和批处理引用编译时不存在的表。这种能力称为延迟名称解析。但是,如果 Transact-SQL 存储过程、触发器或批处理引用在存储过程或触发器中定义的表,则只有当兼容级别设置(通过执行 sp_dbcmptlevel 设置)等于 65 时,才会在创建时发出警告。如果使用批处理,则在编译时发出警告。如果引用的表不存在,将在运行时返回错误信息。有关更多信息,请参见延迟名称解析和编译。
      权限
      CREATE TRIGGER 权限默认授予定义触发器的表所有者、sysadmin 固定服务器角色成员以及 db_owner 和 db_ddladmin 固定数据库角色成员,并且不可转让。
      若要检索表或视图中的数据,用户必须在表或视图中拥有 SELECT 语句权限。若要更新表或视图的内容,用户必须在表或视图中拥有 INSERT、DELETE 和 UPDATE 语句权限。
      如果视图中存在 INSTEAD OF 触发器,用户必须在该视图中有 INSERT、DELETE 和 UPDATE 特权,以对该视图发出 INSERT、DELETE 和 UPDATE 语句,而不管实际上是否在视图上执行了这样的操作。
      示例
      A. 使用带有提醒消息的触发器
      当有人试图在 titles 表中添加或更改数据时,下例将向客户端显示一条消息。
      说明 消息 50009 是 sysmessages 中的用户定义消息。有关创建用户定义消息的更多信息,请参见 sp_addmessage。
      USE pubs
      IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'reminder' AND type = 'TR')
      DROP TRIGGER reminder
      GO
      CREATE TRIGGER reminder
      ON titles
      FOR INSERT, UPDATE 
      AS RAISERROR (50009, 16, 10)
      GO
      B. 使用带有提醒电子邮件的触发器
      当 titles 表更改时,下例将电子邮件发送给指定的人员 (MaryM)。
      USE pubs
      IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'reminder' AND type = 'TR')
      DROP TRIGGER reminder
      GO
      CREATE TRIGGER reminder
      ON titles
      FOR INSERT, UPDATE, DELETE 
      AS
      EXEC master..xp_sendmail 'MaryM', 
      'Don''t forget to print a report for the distributors.'
      GO
      C. 在 employee 和 jobs 表之间使用触发器业务规则
      由于 CHECK 约束只能引用定义了列级或表级约束的列,表间的任何约束(在下例中是指业务规则)都必须定义为触发器。
      下例创建一个触发器,当插入或更新雇员工作级别 (job_lvls) 时,该触发器检查指定雇员的工作级别(由此决定薪水)是否处于为该工作定义的范围内。若要获得适当的范围,必须引用 jobs 表。
      USE pubs
      IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'employee_insupd' AND type = 'TR')
      DROP TRIGGER employee_insupd
      GO
      CREATE TRIGGER employee_insupd
      ON employee
      FOR INSERT, UPDATE
      AS
      /* Get the range of level for this job type from the jobs table. */
      DECLARE @min_lvl tinyint,
      @max_lvl tinyint,
      @emp_lvl tinyint,
      @job_id smallint
      SELECT @min_lvl = min_lvl, 
      @max_lvl = max_lvl, 
      @emp_lvl = i.job_lvl,
      @job_id = i.job_id
      FROM employee e INNER JOIN inserted i ON e.emp_id = i.emp_id 
      JOIN jobs j ON j.job_id = i.job_id
      IF (@job_id = 1) and (@emp_lvl <> 10) 
      BEGIN
      RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)
      ROLLBACK TRANSACTION
      END
      ELSE
      IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)
      BEGIN
      RAISERROR ('The level for job_id:%d should be between %d and %d.',
      16, 1, @job_id, @min_lvl, @max_lvl)
      ROLLBACK TRANSACTION
      END
      D. 使用延迟名称解析
      下例创建两个触发器以说明延迟名称解析。
      USE pubs
      IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'trig1' AND type = 'TR')
      DROP TRIGGER trig1
      GO
      -- Creating a trigger on a nonexistent table.
      CREATE TRIGGER trig1
      on authors
      FOR INSERT, UPDATE, DELETE
      AS 
      SELECT a.au_lname, a.au_fname, x.info 
      FROM authors a INNER JOIN does_not_exist x 
      ON a.au_id = x.au_id
      GO
      -- Here is the statement to actually see the text of the trigger.
      SELECT o.id, c.text
      FROM sysobjects o INNER JOIN syscomments c 
      ON o.id = c.id
      WHERE o.type = 'TR' and o.name = 'trig1'
      -- Creating a trigger on an existing table, but with a nonexistent 
      -- column.
      USE pubs
      IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'trig2' AND type = 'TR')
      DROP TRIGGER trig2
      GO
      CREATE TRIGGER trig2 
      ON authors
      FOR INSERT, UPDATE
      AS 
      DECLARE @fax varchar(12)
      SELECT @fax = phone 
      FROM authors
      GO
      -- Here is the statement to actually see the text of the trigger.
      SELECT o.id, c.text
      FROM sysobjects o INNER JOIN syscomments c 
      ON o.id = c.id
      WHERE o.type = 'TR' and o.name = 'trig2'
      E. 使用 COLUMNS_UPDATED
      下例创建两个表:一个 employeeData 表和一个 auditEmployeeData 表。人力资源部的成员可以修改 employeeData 表,该表包含敏感的雇员薪水信息。如果更改了雇员的社会保险号码 (SSN)、年薪或银行帐户,则生成审核记录并插入到 auditEmployeeData 审核表。
      通过使用 COLUMNS_UPDATED() 功能,可以快速测试对这些包含敏感雇员信息的列所做的更改。只有在试图检测对表中的前 8 列所做的更改时,COLUMNS_UPDATED() 才起作用。
      USE pubs
      IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
      WHERE TABLE_NAME = 'employeeData')
      DROP TABLE employeeData
      IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
      WHERE TABLE_NAME = 'auditEmployeeData')
      DROP TABLE auditEmployeeData
      GO
      CREATE TABLE employeeData (
      emp_id int NOT NULL,
      emp_bankAccountNumber char (10) NOT NULL,
      emp_salary int NOT NULL,
      emp_SSN char (11) NOT NULL,
      emp_lname nchar (32) NOT NULL,
      emp_fname nchar (32) NOT NULL,
      emp_manager int NOT NULL
      )
      GO
      CREATE TABLE auditEmployeeData (
      audit_log_id uniqueidentifier DEFAULT NEWID(),
      audit_log_type char (3) NOT NULL,
      audit_emp_id int NOT NULL,
      audit_emp_bankAccountNumber char (10) NULL,
      audit_emp_salary int NULL,
      audit_emp_SSN char (11) NULL,
      audit_user sysname DEFAULT SUSER_SNAME(),
      audit_changed datetime DEFAULT GETDATE()
      )
      GO
      CREATE TRIGGER updEmployeeData 
      ON employeeData 
      FOR update AS
      /*Check whether columns 2, 3 or 4 has been updated. If any or all of columns 2, 3 or 4 have been changed, create an audit record. The bitmask is: power(2,(2-1))+power(2,(3-1))+power(2,(4-1)) = 14. To check if all columns 2, 3, and 4 are updated, use = 14 in place of >0 (below).*/
      IF (COLUMNS_UPDATED() & 14) > 0
      /*Use IF (COLUMNS_UPDATED() & 14) = 14 to see if all of columns 2, 3, and 4 are updated.*/
      BEGIN
      -- Audit OLD record.
      INSERT INTO auditEmployeeData
      (audit_log_type,
      audit_emp_id,
      audit_emp_bankAccountNumber,
      audit_emp_salary,
      audit_emp_SSN)
      SELECT 'OLD', 
      del.emp_id,
      del.emp_bankAccountNumber,
      del.emp_salary,
      del.emp_SSN
      FROM deleted del
      -- Audit NEW record.
      INSERT INTO auditEmployeeData
      (audit_log_type,
      audit_emp_id,
      audit_emp_bankAccountNumber,
      audit_emp_salary,
      audit_emp_SSN)
      SELECT 'NEW',
      ins.emp_id,
      ins.emp_bankAccountNumber,
      ins.emp_salary,
      ins.emp_SSN
      FROM inserted ins
      END
      GO
      /*Inserting a new employee does not cause the UPDATE trigger to fire.*/
      INSERT INTO employeeData
      VALUES ( 101, 'USA-987-01', 23000, 'R-M53550M', N'Mendel', N'Roland', 32)
      GO
      /*Updating the employee record for employee number 101 to change the salary to 51000 causes the UPDATE trigger to fire and an audit trail to be produced.*/
      UPDATE employeeData
      SET emp_salary = 51000
      WHERE emp_id = 101
      GO
      SELECT * FROM auditEmployeeData
      GO
      /*Updating the employee record for employee number 101 to change both the bank account number and social security number (SSN) causes the UPDATE trigger to fire and an audit trail to be produced.*/
      UPDATE employeeData
      SET emp_bankAccountNumber = '133146A0', emp_SSN = 'R-M53550M'
      WHERE emp_id = 101
      GO
      SELECT * FROM auditEmployeeData
      GO
      F. 使用 COLUMNS_UPDATED 测试 8 列以上
      如果必须测试影响到表中前 8 列以外的列的更新时,必须使用 UBSTRING 函数测试由 COLUMNS_UPDATED 返回的适当的位。下例测试影响 Northwind.dbo.Customers 表中的第 3、第 5 或第 9 列的更新。
      USE Northwind
      DROP TRIGGER tr1
      GO
      CREATE TRIGGER tr1 ON Customers
      FOR UPDATE AS
      IF ( (SUBSTRING(COLUMNS_UPDATED(),1,1)=power(2,(3-1))
      + power(2,(5-1))) 
      AND (SUBSTRING(COLUMNS_UPDATED(),2,1)=power(2,(1-1)))
      ) 
      PRINT 'Columns 3, 5 and 9 updated'
      GO
      UPDATE Customers 
      SET ContactName=ContactName,
      Address=Address,
      Country=Country
      GO
  • controller中的transactions(error,passed,failed)窗口打开不了

    2009-06-12 15:02:15

    在windows目录下有一个wlrun7.ini文件
    修改下面的差数值即可
    [DockablePlace]
    HOST_FRAME_PLACE=201,172,767,576,
    OUTPUT_FRAME_PLACE=81,172,721,539,
    TRANS_FRAME_PLACE=236,93,579,564,
    VUSER_FRAME_PLACE=-16,110,752,502,
  • Loadrunner中参数设置详细分析(二)

    2007-11-28 14:25:09

    Loadrunner中参数设置详细分析(二)

    五、数据文件
      数据文件包含着脚本执行过程中虚拟用户访问的数据。局部和全局文件中都可以存储数据。可以指定现有的ASCII文件、用脚本生成器创建一个新的文件或者引入一个数据库。在参数有很多已知值的时候数据文件非常有用。数据文件中的数据是以表的形式存储的。一个文件中可以包含很多参数值。每一列包含一个参数的数据。列之间用分隔符隔开,比如说,用逗号。
      对数据文件设置参数属性
      如果使用文件作为参数的数据源,必须指定以下内容:文件的名称和位置、包含数据的列、文件格式,包括列的分隔符、更新方法。
     如果参数的类型是“File”,打开参数属性(Parameter Properties)对话框,设置文件属性如下:
    1、 在“File path”中输入文件的位置,或者点击“Browse”指定一个已有文件的位置。缺省情况下,所有新的数据文件名都是“parameter_name.dat”,注意,已有的数据文件的后缀必须是.dat。
    2、 点击“Edit”。记事本打开,里面第一行是参数的名称,第二行是参数的初始值。使用诸如逗号之类的分隔符将列隔开。对于每一新的表行开始一行新的数据。
      注意:在没有启动记事本的情况下如果想添加列,就在参数属性对话框中点击“Add Col”,那么“Add new column”对话框就会弹出。输入新列的名称,点击“OK”。脚本生成器就会添加该列到表中,并显示该列的初始值。
    3、 在“Select Column”部分,指明包含当前参数数据的列。你可以指定列名或者列号。列号是包含你所需要数据的列的索引。列名显示在每列的第一行(row 0)。
    4、 在“Column delimiter”中输入列分隔符,你可以指定逗号、空格符等等。
    5、 在“First data line”中,在脚本执行的时候选择第一行数据使用。列标题是第0行。若从列标题后面的第一行开始的话,那就在“First data line”中输入1。如果没有列标题,就输入0。
    6、 在“Select next row”中输入更新方法,以说明虚拟用户在脚本执行的过程中如何选择表中的数据。方法可以是:连续的、随机的、唯一的、或者与其它参数表的相同行。
    6.1、 顺序(Sequential):该方法顺序地给虚拟用户分配参数值。如果正在运行的虚拟用户访问数据表的时候,它会取到下一行中可用的数据。
    6.2、 随机(Random):该方法在每次迭代的时候会从数据表中取随机数
    6.3、 使用种子取随机顺序(Use Random Sequence with Seed):如果从Loadrunner的控制器来运行scenario,你可以指定一个种子数值用于随机顺序。每一个种子数值在测试执行的时候代表了一个随机数的顺序。无论你何时使用这个种子数值,在scenario中同样的数据顺序就被分配给虚拟用户。如果在测试执行的时候发现了一个问题并且企图使用同样的随机数序列来重复测试,那么,你就可以启动这个功能(可选项)。
    6.4、 唯一(Unique):Unique方法分配一个唯一的有顺序的值给每个虚拟用户的参数。
    6.5 、与以前定义的参数取同一行(Same Line As <parameter>):该方法从和以前定义过的参数中的同样的一行分配数据。你必须指定包含有该数据的列。在下拉列表中会出现定义过的所有参数列表。注意:至少其中的一个参数必须是Sequential、Random或者Unique。

    如果数据表中有三列,三个参数定义在列表中:id1,name1和title1,如下:。
    ID Name Title
    132 Kim Manager
    187 Cassie Engineer
    189 Jane VP
        对于参数id1,你可以指示虚拟用户使用Random方法,而为参数name1和title1就可以指定方法“Same Line as id1”。所以,一旦ID“132”被使用,那么,姓名(Name)“Kim”和职位(Title)“Manager”同时被使用。

    7、Updta value on数据的更新方法
    7.1、Each iteration――每次反复都要取新值
    7.2、Each occurrence――只要发现该参数就重新取值
    7.3、Once――在所有的反复中都使用同一个值
    8、When out of values超出范围:(选择数据为unique时才可用到)
    8.1、Abort Vuser――中止
    8.2、Continue in a cyclic manner――继续循环取值
    8.3、Continue with last value――取最后一个值
    9、Allocate Vuser values in the Controller在控制器中分配值:(选择数据为unique时才可用到)
    9.1、 Automatically allocate block size――自动分配
    9.2、Allocate()values for each Vuser――指定一个值

    六、从已存在的数据库中导入数据

    Loadrunner允许你利用参数化从已经存在的数据库中导入数据。可以使用下列两种方式之一:
    1、 使用Microsoft Query(要求在系统上先安装MS Query)。
    2、 指定数据库连接字符串和SQL语句。
        用户脚本生成器在从数据库中导入数据的过程中提供了一个向导。在向导中,你指明如何导入数据-通过MS Query创建查询语句或者直接书写SQL语句。在导入数据以后,以.dat为后缀并作为正规的参数文件保存。要开始导入数据库中数据的过程,在参数属性对话框中点击“Data Wizard”,则,数据库查询向导弹出。
      要创建新的查询
    1、 选择“Create new query”。如果需要MS Query的帮助,选择“Show me how to use Microsoft Query”,然后点击“Finish”。
    如果你还没有安装Microsoft Query,Loadrunner会提示你这个功能不可用。在进行之前,从Microsoft Office中安装MS Query。
    2、 在Microsoft Query中遵循以下步骤,导入期望的表和列。
    3、 在完成数据的导入后,选择“Exit and return to Virtual User Generator”,然后点击“Finish”。在参数属性对话框中数据库记录以data文件的形式显示出来。
    要在MS Query中编辑并查看数据,选择“View data or edit in Microsoft Query”。若要结束,则选择“File>Exit and return to Virtual User Generator”返回到脚本生成器。
    4、 在“Select Column”部分,指定包含当前参数数据的列可以指定列号或者列名。注意:列标题默认为第0行(row 0)。
    5、 从“Select next row”列表中选择一个更新方法来告诉虚拟用户在脚本指定的过程中如何选择表中的数据。可选项是:Sequential、Random、Unique或者Same Line As。其中每一项的含义文章前面已经讲述,就不再赘述。
    6、 如果选择“Advance row each iteration”,虚拟用户在每次迭代的时候会使用新的一行的数据而不是重复同样的数据。
      要指定数据库连接或者SQL语句
    1、 选择“Specify SQL Statement”,然后点击“Next”。
    2、 点击“Create”指定一个新的连接字符串。选择数据源的窗口弹出。
    3、 选择已有的数据源,或者点击“New”创建一个新的数据源。向导将提示你穿过创建ODBC数据源的过程。在完成后,连接字符串就会在连接字符串框中显示出来。
    4、 在SQL框中,输入或者粘贴SQL语句。
    5、 点击“Finish”继续SQL语句并导入数据。数据库记录将以data文件的形式显示在参数属性框中。
    6、 在“Select Column”部分中,指定包含当前参数数据的列。你可以指定列号或者列名。
    7、 从“Select next row”列表中选择一个更新方法来告诉虚拟用户在脚本指定的过程中如何选择表中的数据。可选项是:Sequential、Random、Unique或者Same Line As。

  • Loadrunner中参数设置详细分析(转)

    2007-11-28 14:23:09

    Loadrunner中参数设置详细分析(一)

    相信对大家会有用的,这个版本是基于7.8的。
    做负载或者压力测试时,很多人选择使用了Loadrunner测试工具。该工具的基本流程是先将用户的实际操作录制成脚本,然后产生数千个虚拟用户运行脚本(虚拟用户可以分布在局域网中不同的PC机上),最后生成相关的报告以及分析图。但是在录制脚本的过程中会遇到很多实际的问题,比如不同的用户有不同的使用数据,这就牵涉到参数的设置问题。本文就Loadrunner中参数的设置进行说明,希望对大家有所帮助。
       
    在录制程序运行的过程中,VuGen(脚本生成器) 自动生成了包含录制过程中实际用到的数值的脚本。如果你企图在录制的脚本中使用不同的数值执行脚本的活动(如查询、提交等等),那么你必须用参数值取代录制的数值。这个过程称为参数化脚本。
       
    本文主要包括如下内容:理解参数的局限性、建立参数、定义参数的属性、理解参数的类型、为局部数据类型设置参数的属性、为数据文件设置参数的属性、从已经存在的数据库中引入数据。
       
    除了GUI,以下的内容适合于各种类型的用户脚本。
    一、关于参数的定义

       
    在你录制程序运行的过程中,脚本生成器自动生成由函数组成的用户脚本。函数中参数的值就是在录制过程中输入的实际值。
        例如,你录制了一个
    Web应用程序的脚本。脚本生成器生成了一个声明,该声明搜索名称为“UNIX”的图书的数据库。当你用多个虚拟用户和迭代回放脚本时,也许你不想重复使用相同的值“UNIX”。那么,你就可以用参数来取代这个常量。结果就是你可以用指定的数据源的数值来取代参数值。数据源可以是一个文件,也可以是内部产生的变量。
       
    用参数表示用户的脚本有两个优点:
     可以使脚本的长度变短。
     可以使用不同的数值来测试你的脚本。例如,如果你企图搜索不同名称的图书,你仅仅需要写提交函数一次。在回放的过程中,你可以使用不同的参数值,而不只搜索一个特定名称的值。
       
    参数化包含以下两项任务:
     在脚本中用参数取代常量值。
     设置参数的属性以及数据源。
        参数化仅可以用于一个函数中的参量。你不能用参数表示非函数参数的字符串。另外,不是所有的函数都可以参数化的。

     

    二、参数的创建
       
    可以指定名称和类型来创建参数。不存在对脚本中参数个数的限制。在Web程序的用户脚本中,你可以使用如下过程在基于文本的脚本视图中创建参数。或者,也可以在基于图标的树形视图中创建参数。
       
    在基于文本的脚本视图中创建一个参数:
    1
     将光标定位在要参数化的字符上,点击右键。打开弹出菜单。
    2
     在弹出菜单中,选择“Replace with a Parameter”。选择或者创建参数的对话框弹出。
    3
     在“Parameter name”中输入参数的名称,或者选择一个在参数列表中已经存在的参数。
    4
     在“Parameter type”下拉列表中选择参数类型。
    5
     点击“OK”,关闭该对话框。脚本生成器便会用参数中的值来取代脚本中被参数化的字符,参数用一对“{}”括住。
        注意:在参数化
    CORBA或者General-Java 用户脚本的时候,必须参数化整个字符串,而不是其中的部分。另外注意:除了Web或者WAP,缺省的参数括号对于任何脚本都是 {}”。你可以在“General Options”对话框中的“Parameterization”标签(Tools>General Options)中定义参数括号种类。
    6
     用同样的参数替换字符的其余情况,选中参数,点击右键,弹出菜单。从弹出的菜单中,选择“Replace More Occurrences”。搜索和替换对话框弹出。“Find What”中显示了你企图替换的值。“Replace With”中显示了括号中参数的名称。选择适当的检验框来匹配整个字符或者大小写。如果要搜索规则的表达式(.!?等等),选中“Regular Expression”检验框,然后点击“Replace”或者“Replace All”。
        注意:小心使用“
    Replace All”,尤其替换数字字符串的时候。脚本生成器将会替换字符出现的所有情况。
    7
     如果想用以前定义过的参数来替换常量字符串的话,选中该字符串,点击右键,然后选择“Use Existing Parameter”,子菜单“Use Existing Parameters”弹出。从子菜单“Use Existing Parameters”选择参数,或者用“Select from Parameter List”来打开参数列表对话框。
        注意:如果用以前定义过的参数来替换常量字符串的话,那么,使用“
    Parameter List”非常方便。同时,还可以查看和修改该参数的属性。
    8
     对于已经用参数替换过的地方,如果想取回原来的值,那么,就在参数上点击右键,然后选择“Restore Original value”。
       
    Web用户脚本的树形视图中创建参数:
    1
    、将光标定位在企图参数化的地方,点击右键,从弹出的菜单中选择“Properties”。则相关的属性对话框打开。
    2、点击在要参数化的参量的旁边的“
    ABC”形状的图标。“Select or Create Parameter”对话框打开。
    3、在“
    Parameter name”中输入参数的名称,或者从列表中选择一个已经存在的参数。
    4、在“
    Parameter type”中输入参数的类型。
    5、点击“
    OK”关闭该对话框。用户脚本生成器会用参数来替换最初的字符串常量,并用一个表格形状的图标替换“ABC”形状的图标。
    6、要恢复参数化以前的值,点击图标,然后从弹出的菜单中选择“
    Undo Parameter”,则以前的值便会重现。

     

    三、定义参数的属性
       
    创建参数完成后,就可以定义其属性了。参数的属性定义就是定义在脚本执行过程中,参数使用的数据源。在Web用户脚本中,你既可以在基于文本的脚本视图中定义参数属性,也可以在基于图标的树形视图中定义参数属性。下面的过程将教你如何在基于本文的脚本视图中定义参数属性。
       
    在基于文本的脚本视图中定义参数属性步骤:
    1
     在参数上点击右键,有菜单弹出。
    2
     在弹出的菜单中,选择“Parameter Properties”。参数属性对话框打开,显示和当前参数类型相关的属性。
    3
     输入参数的属性值。
    4
     点击“Close”关闭参数属性对话框。
        
    Web用户脚本的树形视图中定义参数的属性:
    1
     将关标定位在参数上,然后点击右键,选择“Properties”。属性对话框打开。
    2
     点击要定义属性的参数旁边的表格形状按钮,点击右键,选择“Parameter Properties”。参数属性对话框打开,和参数类型相关的属性显示出来。
    3
     输入参数的属性。
    4
     点击“Close”关闭参数属性对话框。
       
    使用参数列表:
      使用参数列表可以在任意时刻查看所有的参数,创建新的参数、删除参数,或者修改已经存在参数的属性。
    1
     点击参数列表按钮或者用“Vuser>Parameter List”。参数列表对话框打开。
    2
     要创建新的参数,点击“New”按钮。新的参数则被添加在参数树中,该参数有一个临时的名字,你可以给它重新命名,然后回车。设置参数的类型和属性,点击“OK”,关闭参数列表对话框。
       
    注意:不要将一个参数命名为“unique”,因为这个名称是用户脚本生成器本身的。用户脚本生成器创建新的参数,但是不会自动用该参数在脚本中替换任意选中的字符串。
    3
     要删除已有的参数,那么,要先从参数树中选择该参数,点击“Delete”,然后确认你的行为即可。
    4
     要修改已有参数,那么,要先从参数树中选择该参数,然后编辑参数的类型和属性。

    四、理解参数的类型
      在你定义参数属性的时候,要指定参数值的数据源。你可以指定下列数据源类型的任何一种:
    Internal Data
    ―― 虚拟用户内部产生的数据。
    Data Files 
    ――存在于文件中的数据。可能是已存在的文件或者是用脚本生成器新创建的。
    User-Defined Functions
    ―― 调用外部DLL函数生成的数据
      Internal Data包括以下几种:
    1
     Date/Time
      Date/Time用当前的日期/时间替换参数。要指定一个Date/Time格式,你可以从菜单列表中选择格式,或者指定你自己的格式。这个格式应该和你脚本中录制的Date/Time格式保持一致。
    2
     Group Name
      Group Name 用虚拟用户组名称替换参数。在创建scenario的时候,你可以指定虚拟用户组的名称。当从用户脚本生成器运行脚本的时候,虚拟用户组名称总是None
    3
     Load Generator Name
      Load Generator Name用脚本负载生成器的名称替换参数。负载生成器是虚拟用户在运行的计算机。
    4. Iteration Number
      Iteration Number用当前的迭代数目替换参数。
    5
     Random Number
      Random Number用一个随机数替换参数。通过指定最大值和最小值来设置随机数的范围。
    6
     Unique Number
      Unique Number用一个唯一的数字来替换参数。你可以指定一个起始数字和一个块的大小。
    7
     Vuser ID
      Vuser ID用分配给虚拟用户的ID替换参数,ID是由Loadrunner的控制器在scenario运行时生成的。如果你从脚本生成器运行脚本的话,虚拟用户的ID总是-1

  • Begin

    2007-11-19 18:36:51

    开始了!

Open Toolbar