ASP 与 DATABASE (1)

ASP作为一种Web应用程序,其核心技术就是数据库编程技术。数据库是存储数据的重要工具,其中的数据按照一定的数据模型组织和描述进行存储。数据库存在多种模型,而应用于大型数据储存的数据库一般分为网状数据库(Network Database)、关系数据库(Relational Database)、树状数据库(Hierarchical Database)以及面向对象型数据库等。数据库的数据是由数据库管理系统DBMS(Database Manage System)管理和控制的,它提供了数据的安全性保护、完整性检查、并发控制以及数据库恢复等功能。常见的数据库管理系统有Microsoft Access、MS-SQL Server、MySQL、DB2、Oracle等。

ASP具有功能强大的服务器端脚本编程环境,利用ASP内置的ADO组件,可以轻松地访问和操作各种数据库。ASP对数据库的整个访问过程是:客户端浏览器通过ASP页面向Web服务器发出请求,服务器接受请求并使用ASP.DLL文件对提交的页面程序进行解释,然后通过指定的接口连接数据库服务器,对数据库系统进行访问并执行相关操作,数据库服务器将执行的结果返回给Web服务器,Web服务器再将此结果生成动态的网页返回给客户端浏览器,如图2.1所示。

图2.1  ASP访问数据库流程图

本章将针对典型数据库来介绍数据库连接方案、数据库操作方案以及数据库维护方案。

2.2  数据库连接方案

在数据库发展的初级阶段,一个数据库只能针对一个应用程序,即开发数据库时只提供与该应用程序的连接接口,如果应用程序发生变化,就不能保证与数据库的有效连接。随着技术的不断发展和进步,数据库的独立性和共享性不断增强,应用程序可以通过统一的接口与数据库建立连接。

使用ASP开发动态网站时,主要是通过ADO组件与数据库建立连接。根据实际情况的不同,可以确定是使用ODBC接口还是使用OLE DB接口进行连接。在本节中,将介绍ASP与Access数据库、SQL Server数据库以及Oracle数据库的连接方案,每个方案中都将具体说明如何根据实际的开发环境选择适合的连接方法与指定的数据库建立有效、安全的连接。

2.2.1  连接Access数据库

Access数据库提供了一组功能强大的工具,通过Access可以创建功能完备的数据库解决方案。使用Access数据库作为ASP应用程序的后台数据存储工具,不仅可以开发个人信息管理方面的网站,还可以开发中小型企业的采购销售、仓库管理、生产管理、财务管理等方面的网站。下面将详细介绍ASP连接Access数据库的方法和实现过程。

1.方案分析

ASP通过与Access数据库建立有效的连接,来操作数据库中的数据。在ASP中,应首先确定连接数据库语句,然后创建Connection对象并调用其Open方法来连接Access数据库。连接Access数据库有3种常用方法:使用无ODBC DSN连接、使用ODBC连接和使用OLE DB连接。为了使读者更好的理解ASP连接Access数据库的过程,下面给出连接Access数据库的流程图,如图2.4所示。

图2.4  连接Access数据库的流程图

2.实施过程

在开发以Access数据库作为后台数据库的动态网站时,程序设计者会根据网络服务器的配置以及网站性质确定连接Access数据库的方法。在实际应用中,通常将连接数据库的语句段单独建立为一个.asp文件,在需要操作数据库的页面中使用#include指令调用此文件即可。以连接客房预定系统网站为例,运行效果如图2.5所示。

图2.5  连接Access数据库

下面分别介绍连接Access数据库的3种方法。

l          方法一  无ODBC DSN连接

*  实例位置:光盘"mr"2"2.2"2.2.1"01

一般情况下,通过无ODBC DSN连接方法可以快捷地连接Access数据库,因为ADO提供了强大的数据库访问技术,只要保证服务器上安装了Access数据库的驱动程序,ASP通过ADO在无须配置ODBC DSN的情况下,就可以很方便地与Access数据库建立连接。

无ODBC DSN连接Access数据库的代码如下:

例程2-1  代码位置:光盘"mr"2"2.2"2.2.1"01"conn.asp

<%

  Set Conn=Server.CreateObject("ADODB.Connection")  '创建名为Conn的Connection对象

  Conn.Open("Driver={Microsoft Access Driver (*.mdb)};DBQ="&Server.mappath("DataBase/db.mdb")&"")  '建立连接

%>

Driver:用于指定Access数据库的驱动程序。

DBQ:用于指定Access数据库的完整路径以及数据库名称。

通过调用Server对象的MapPath方法可以返回指定虚拟目录在Web服务器上的真实物理路径。

为了保证Access数据库的正常运行,维护数据安全,可以为建立的Access数据库设置密码。通过无ODBC DSN方法连接设有密码的Access数据库的代码如下:

<%

  Set Conn=Server.CreateObject("ADODB.Connection")  '创建名为Conn的Connection对象

  Conn.Open("Driver={Microsoft Access Driver (*.mdb)};DBQ="&Server.mappath("DataBase/db.mdb")&";pwd=123456;")  '建立连接

%>

l          方法二  通过ODBC连接

*  实例位置:光盘"mr"2"2.2"2.2.1"02

创建Access数据库后,将会产生一个.mdb数据库文件,此文件单独存储在服务器上。如果使用该数据库的ASP应用程序存在安全漏洞,网站攻击者就会通过连接数据库的语句获知Access数据库所在的物理位置,从而很容易下载该数据库。为了更好地保护Access数据库,并确保与数据库的有效连接,可以通过配置系统DSN或者文件DSN使用ODBC方法连接数据库,这样不但可以隐藏数据库的实际位置,还可以防止站点中文件源代码的泄漏。

下面以Windows 2003操作系统为例,介绍在ODBC数据源管理器中配置系统DSN连接指定的Access数据库。关键操作步骤如下:

(1)单击“开始”按钮,选择“程序”→“管理工具”→“数据源(ODBC)”命令,打开“ODBC数据源管理器”对话框,打开“系统DSN”选项卡。

(2)单击“添加”按钮,打开“创建新数据源”对话框,选择安装数据源的驱动程序,这里选择“Microsoft Access Driver (*.mdb)”,如图2.6所示。

图2.6  选择安装数据源的驱动程序

(3)单击“完成”按钮,打开“ODBC Microsoft Access 安装”对话框,填写“数据源名”及相关“说明”,并指定所要连接数据库的路径,如图2.7所示。

图2.7  “ODBC Microsoft Access 安装”对话框

(4)单击“确定”按钮,完成配置系统DSN的操作。

使用ODBC方法连接Access数据库的代码如下:

例程2-2  代码位置:光盘"mr"2"2.2"2.2.1"02"conn.asp

<%

  Dim Conn

  Set Conn=Server.CreateObject("ADODB.Connection")

  Conn.Open "DSN=AccessDSN" 

%>

如果Access数据库设有密码,可以使用以下代码连接数据库:

<%

  Dim Conn

  Set Conn=Server.CreateObject("ADODB.Connection")

  Conn.Open "DSN=AccessDSN;uid=admin;pwd=123456;" 

%>

l          方法三  通过OLE DB连接

*  实例位置:光盘"mr"2"2.2"2.2.1"03

在实际应用中,通过OLE DB连接数据库的速度比较快,如果需要访问的数据库提供了使用OLE DB的程序,建议使用OLE DB方法连接数据库,代码如下:

例程2-3  代码位置:光盘"mr"2"2.2"2.2.1"03"conn.asp

<%

  Dim Conn,ConnStr

  Set Conn=Server.CreateObject("ADODB.Connection")

  ConnStr="Provider=Microsoft.Jet.OLEDB.4.0;Data Source="&Server.mappath("DataBase/db.mdb")&";User ID=admin;Password=;"

  Conn.Open(ConnStr)  '建立连接 

%>

以上各参数的说明如表2.11所示。

表2.11                            参数说明

参数

描述

Provider

表示数据源的提供者

Data Source

用于指定打开的数据库文件,它必须是完整的数据库路径

User ID

可选的字符串,是数据源设定的具有访问权限的用户名称

Password

用户密码,对应于在User ID中指定用户的数据库访问密码

如果为Access数据库设置了密码,则可使用以下连接语句:

<%

  Set Conn=Server.CreateObject("ADODB.Connection")

  ConnStr="Provider=Microsoft.Jet.OLEDB.4.0;Data Source="&Server.mappath("DataBase/db.mdb")&";Jet OLEDB:DataBase Password=123456;admin,"""

  Conn.Open(ConnStr)

%>

以上介绍了3种连接Access数据库的方法,每种方法都有各自的特点以及适合的环境,读者可以在实际的应用中选择适合的数据库连接方法。

3.补充说明

Access数据库文件很容易被下载,为了确保与Access数据库的有效连接,在此提出几个在创建Access数据库时需要注意的事项:

(1)在创建表时,定义一个类型为OLE对象的字段,并使其内容为字符串“<%”,这样当非法用户下载Access数据库时,系统会解释其内容发现语法错误以禁止用户的操作;然后将.mdb数据库文件重新命名为.asp文件,因为.asp文件对应的可执行文件为asp.dll(即已为扩展名.asp添加了应用程序扩展名映射),这样可以保证asp文件不被下载。

(2)将Access数据库文件命名为global.asa,在此之前应为.asa文件添加应用程序扩展名映射,命名后的Access数据库文件不能放置在网站的根目录下。

(3)将Access数据库文件放置在虚拟目录以外,即放置在同一网站的另一个虚拟目录中,这时可以使用#Include指令以及Virtual关键字来指定连接数据库的文件路径。

如果Access数据库文件被重新命名,在连接数据库语句中直接使用命名后的文件名即可。

2.2.2  连接SQL Server数据库

MS-SQL Server是Microsoft公司设计开发的一种关系型数据库管理系统。SQL Server的核心是用来处理数据库命令的SQL Server引擎,此引擎运行在Windows操作系统环境下,只对数据库连接和SQL命令进行处理。SQL Server不仅拥有一个功能强大并且稳定的引擎,它还提供了一系列用于管理数据库服务器的工具,以及用于转换和移动数据、实现数据仓库和数据分析的附加软件,并在客户端和服务器端都提供了用于管理数据库连接的服务。ASP不但可以连接Access数据库,还可以连接SQL Server数据库进行一些复杂的数据操作。下面详细介绍ASP连接SQL Server数据库的方法和实现过程。

1.方案分析

SQL Serve数据库可以运行在工作站、数据库服务器和网络上。使用ASP开发的Web应用程序,可以使用SQL Server作为网站的后台数据库。ASP通过与SQL Server数据库建立有效的连接,来操作和维护数据库中的数据。常用的连接SQL Server数据库的方法有3种,分别是通过无ODBC DSN连接、通过ODBC连接和通过OLE DB连接。根据连接过程,给出连接SQL Server数据库的流程图,如图2.8所示。

图2.8  连接SQL Server数据库的流程图

2.实施过程

用SQL Server作为ASP网站的后台数据库,不仅可以大容量地存储文本、图像等数据,还可以灵活地使用SQL Server的强大功能对数据进行查询、维护等操作,并提高数据存储的安全性。以连接电子商城网站为例,运行效果如图2.9所示。

图2.9  连接SQL Server数据库

下面分别介绍连接SQL Server数据库的3种方法。

l          方法一  无ODBC DSN连接

*  实例位置:光盘"mr"2"2.2"2.2.2"01

ADO是当前微软公司所支持的操作数据库的有效、简单而且功能强大的一种方法。在ASP应用程序中通过无ODBC DSN方法不仅可以连接Access数据库,还可以访问SQL Server数据库。

通过无ODBC DSN方法建立与SQL Server数据库连接,代码如下:

例程2-4  代码位置:光盘"mr"2"2.2"2.2.2"01"conn.asp

<%

Dim Conn,Connstr

Set Conn=Server.CreateObject("ADODB.Connection") '创建名为Conn的Connection对象

Connstr ="Driver={SQL Server};Server=(local);Uid=sa;Pwd=;Database=db_02" '定义连接数据库字符串

Conn.Open(Connstr)      '建立连接

%>

以上各参数的说明如表2.12所示。

表2.12                            参数说明

参数

描述

Driver

SQL Server数据库的驱动程序

Server

在IIS服务器上建立的访问SQL Server服务器的别名

Uid

访问SQL Server数据库使用的用户名称

Pwd

访问SQL Server数据库使用的用户口令

Database

访问的数据库名称

l          方法二  使用ODBC连接

*  实例位置:光盘"mr"2"2.2"2.2.1"02

在数据安全要求比较高并且用户有操控服务器权限的情况下,可以使用ODBC方法连接SQL Server数据库。使用ODBC访问SQL Server数据库,需要配置ODBC数据源DSN,它把使用的数据库驱动程序、数据库、用户名、口令等信息组合在一起,以供应用程序调用。数据源名称DSN有3种类型,分别是用户DSN、系统DSN和文件DSN。一般情况下配置系统DSN,因为它不仅支持Web数据库应用程序,还允许所有用户访问连接的数据库。

下面以Windows 2003操作系统为例,介绍在ODBC数据源管理器中配置系统DSN连接指定的SQL Server数据库。关键操作步骤如下:

(1)单击“开始”按钮,选择“程序”→“管理工具”→“数据源(ODBC)”命令,打开“ODBC数据源管理器”对话框,选择“系统DSN”选项卡。

(2)单击“添加”按钮,打开“创建新数据源”对话框,选择安装数据源的驱动程序,这里选择“SQL Server”,如图2.10所示。

图2.10  “创建新数据源”对话框

(3)单击“完成”按钮,打开“创建到SQL Server的新数据源”对话框,在“名称”文本框中设置数据源名称为“SqlDSN”;在“描述”文本框设置数据源描述为“配置SQL Server数据库DSN”;在“服务器”下拉列表框中选择连接的数据库所在服务器为“(local)”,如图2.11所示。

图2.11  “创建到SQL Server的新数据源”对话框

(4)单击“下一步”按钮,选中“使用用户输入登录ID和密码的SQL Server验证”单选按钮,在“登录ID”文本框中输入SQL Server用户登录ID,这里为“sa”,在“密码”文本框中输入SQL Server用户登录密码,这里为空密码。

(5)单击“下一步”按钮,选择“更改默认的数据库为”复选框,并在其下拉列表框中选择连接的数据库名称,这里选择“db_02”,单击“下一步”按钮。

(6)单击“完成”按钮,打开“ODBC Microsoft SQL Server安装”对话框,显示新创建的ODBC数据源配置信息,如图2.12所示。

图2.12  显示创建的ODBC数据源配置信息

(7)单击“测试数据源”按钮测试数据库连接是否成功,如果测试成功,单击“确定”按钮,完成数据源配置。

配置系统DSN后,设置Connection对象的Connectionstring属性,并调用Open方法连接SQL Server数据库,代码如下:

例程2-5  代码位置:光盘"mr"2"2.2"2.2.2"02"conn.asp

<%

Dim Conn

Set Conn=Server.CreateObject("ADODB.Connection")  '创建名为Conn的Connection对象

Conn.Connectionstring="DSN=SqlDSN;UID=sa;PWD=;"  '定义连接数据库字符串,赋给Connection对象的ConnectionString属性

Conn.Open '建立连接

%>

l          方法三  使用OLE DB连接

*  实例位置:光盘"mr"2"2.2"2.2.2"03

为了提高程序的运行效率,保证网站浏览者能够以较快地速度打开并顺畅地浏览网页,可以通过OLE DB方法连接SQL Server数据库。OLE是一种面向对象的技术,利用这种技术可以开发可重用软件组件。使用OLE DB不仅可以访问数据库中的数据,还可以访问电子表格Excel、文本文件、邮件服务器中的数据等。使用OLE DB访问SQL Server数据库的代码如下:

例程2-6  代码位置:光盘"mr"2"2.2"2.2.2"03"conn.asp

<%

Dim Conn,Connstr

Set Conn=Server.CreateObject("ADODB.Connection") '创建名为Conn的Connection对象

Connstr="provider=sqloledb;data source=(local);initial catalog=db_02;user id=sa;password=;" '定义连接数据库字符串

Conn.Open Connstr '建立连接

%>

以上连接语句中的各参数说明如表2.13所示。

表2.13                             参数说明

参数

描述

Provider

表示数据源提供者

data source

表示服务器名,如果是本地机器,可以设置成“(local)”

initial catelog

表示数据源名称

user id

可选的字符串,是数据源设定的具有访问权限的用户名称

password

用户密码,对应于user id用户的数据库访问密码

以上介绍了3种常用的SQL Server数据库连接方法,读者可以根据应用程序的运行环境选择适当的连接方法。

3.补充说明

在开发动态网站时,经常是将连接数据库的语句单独放置在一个文件中,需要连接数据库的网页使用#include指令包含该文件即可。为了节省程序开发时间和防止连接数据库文件中程序代码的泄漏,可以将连接数据库的程序放置在Global.asa文件中,而不必在每个文件中都书写包含数据库连接文件的语句。

Global.asa文件是用来存放执行任何ASP应用程序期间的Application、Session事件程序,当Application或者Session对象被第一次调用或者结束时,就会执行该Global.asa文件内的对应程序。在Global.asa文件的Application_Onstart事件中定义连接数据库的相关语句,并将创建的Connection实例变量赋予一个Application变量,当网站中的网页需要操作数据库时可以直接引用该Application变量与数据库建立连接;在Application_Onend事件中,关闭与数据库的连接并释放连接数据库的所有资源,以释放对象所占有的内存并缩短数据库调整资源的时间跨度。

在Global.asa文件中,用户必须使用ASP所支持的脚本语言并且定义在<Script>标记之内,不能定义非Application对象或者Session对象的模板,否则将产生执行上的错误。Global.asa文件中的代码如下:

<Script Language="VBScript" Runat="Server">

Sub Application_Onstart()     

   Dim Conn,ConnStr

Set Conn = Server.CreateObject("ADODB.Connection")

   Connstr ="Driver={SQL Server};Server=(local);Uid=sa;Pwd=;Database=db_02" '这里可以使用其他连接数据库的方式      

   Conn.open ConnStr

   Application("conn")=Conn

End Sub

</script>

<script language="vbscript" runat="server">

Sub Application_Onend

   Conn.close

   Set Conn=Nothing

End Sub

</script>

在ASP文件中,可以直接使用在Global.asa文件中定义的Connection对象对数据库进行访问,代码如下:

<%

Dim rs,sql

   set rs=server.CreateObject("adodb.recordset")

   sql="select Mname from tb_manager"

   rs.open sql,Application("conn"),1,1     

%>

*  注意:一个应用程序只能对应一个Global.asa文件,并且Global.asa文件应存放在网站的根目录下运行。

2.2.3  连接Oracle数据库

Oracle是当前应用最广泛的大型数据库之一。随着技术的不断进步,Oracle数据库在群集技术、高可用性、商业智能、安全性、系统管理等方面都实现了新的突破,成为一个功能较完善的数据库系统。下面介绍ASP连接Oracle数据库的方法和实现过程。

1.方案分析

在开发互联网应用、电子商务网站、互联网管理系统时,可以结合使用ASP和Oracle数据库。ASP是微软公司为开发互联网应用程序所提出的工具之一,ASP与Oracle数据库的连接可以通过ADO组件来实现的,也可以通过Oracle数据库提供的Oracle Objects for OLE数据库服务工具来实现。Oracle Objects for OLE是Oracle开发的一个中间件,它允许使用微软的OLE标准的客户端应用程序访问Oracle数据库;它完全支持PL/SQL,PL/SQL是Oracle对SQL命令集的扩展,这样开发人员就能够利用它对非结构化的SQL命令块进行流控制和逻辑设计。

ASP通过ADO组件与Oracle数据库建立连接有2种常用方法,分别为ODBC方法和OLE DB方法;另外,使用Oracle自带的Oracle Objects for OLE中间件也可以连接Oracle数据库。根据连接方法和连接过程,给出连接Oracle数据库的流程图,如图2.13所示。

图2.13  连接Oracle数据库的流程图

2.实施过程

Oracle是目前使用最广泛的数据库管理系统之一,它可以运行在各种平台上,例如:Windows、UNIX、Linux等。使用Oracle作为ASP网站的后台数据库,可以综合运用ASP操作数据的灵活性和Oracle的强大应用开发功能、完备的安全特性,开发出更符合客户需求的多功能动态网站。以连接企业物流网站为例,运行效果如图2.14所示。

图2.14  连接Oracle数据库

下面分别介绍连接Oracle数据库的3种方法。

l          方法一  通过ODBC连接

*  实例位置:光盘"mr"2"2.2"2.2.3"01

通过ODBC方法可以更安全地连接Oracle数据库。下面以Windows 2003操作系统为例,介绍在ODBC数据源管理器中配置系统DSN连接指定的Oracle数据库。关键操作步骤如下:

(1)单击“开始”按钮,选择“程序”→“管理工具”→“数据源(ODBC)”命令,打开“ODBC数据源管理器”对话框,选择“系统DSN”选项卡。

(2)单击“添加”按钮,打开“创建新数据源”对话框,选择安装数据源的驱动程序,这里选择“Oracle in OralHome92”,如图2.15所示。

图2.15  选择安装数据源的驱动程序

(3)单击“完成”按钮,打开“Oracle ODBC Dirver Configuration”对话框,填写Data SourceName(数据源名称)、Description(描述)、TNS Service Name(Oracle数据库名称)及User(访问Oracle数据库的用户名),如图2.16所示。

图2.16  “Oracle ODBC Dirver Configuration”对话框

(4)单击“Test Connection”按钮,打开“Oracle ODBC Dirver Connect”对话框,输入用户口令,单击“OK”按钮,测试是否能与指定的Oracle数据库建立有效的连接,如图2.17所示。

图2.17  测试与Oracle数据库的连接

(5)如果测试成功,则在“Oracle ODBC Dirver Configuration”对话框中单击“OK”按钮,完成配置系统DSN的操作。

使用ODBC方法连接Oracle数据库的代码如下:

例程2-7  代码位置:光盘"mr"2"2.2"2.2.3"02"conn.asp

<%

  Set Conn=Server.CreateObject("ADODB.Connection")  '创建名为Conn的Connection对象

  Conn.Open("DSN=OracleDSN;uid=scott;pwd=tiger;")  '建立连接

%>

l          方法二  通过OLE DB连接

*  实例位置:光盘"mr"2"2.2"2.2.3"02

Oracle数据库允许程序语言通过OLE DB接口进行访问,其连接数据库代码如下:

例程2-8  代码位置:光盘"mr"2"2.2"2.2.3"03"conn.asp

<%

  Set Conn=Server.CreateObject("ADODB.Connection")  '创建名为Conn的Connection对象

  Conn.Open("Provider=OraOLEDB.Oracle.1;Data Source=dbase;User ID=scott;Password=tiger;Persist Security Info=True")  '建立连接

%>

以上连接语句中的各参数说明如表2.14所示。

表2.14                             参数说明

参数

描述

Provider

表示数据源提供者

data source

表示访问的Oracle数据库名称

user id

表示数据源设定的具有访问权限的用户名称

password

表示用户密码,对应于user id用户的数据库访问密码

Persist Security Info

表示连接成功后是否保存安全信息,True为保存,False为不保存

*  注意:数据源提供者Provider的参数值,根据安装的Oracle数据库版本的不同而不同。读者在实际应用中可以根据实际情况替换此处的参数值。

l          方法三  通过Oracle Objects for OLE连接

*  实例位置:光盘"mr"2"2.2"2.2.3"03

以上介绍了通过ADO组件连接Oracle数据库的几种方法,下面介绍通过Oracle Objects for OLE中间件连接数据库的方法。在ASP中,首先通过Server对象的CreateObject方法创建OraSession对象(该对象是由Oracle Objects for OLE提供,管理应用程序的OraDataBase、OraConnection和OraDynaset对象的),然后再调用其dbopendatabase方法或者opendatabase方法创建一个OraDataBase对象(该对象表示对数据库服务器一个虚拟的登入)完成与Oracle数据库的连接,代码如下:

例程2-9  代码位置:光盘"mr"2"2.2"2.2.3"04"conn.asp

<%

set OraSession=Server.createobject("OracleInprocServer.XOraSession")

set Oradatabase=OraSession.dbopendatabase("dbase","scott/tiger",0)

%>

其中,调用的OraSession.dbopendatabase方法的参数分别表示数据库别名、用户名/用户口令、启动类型。

*  注意:创建OraSession对象时,请确定已安装Oracle客户端。

3.补充说明

为了更有效的连接Oracle数据库,根据程序的运行环境,可以选择使用ADO组件、使用Oracle Objects for OLE中间件或者同时结合两者连接Oracle数据库。下面介绍使用ADO组件和使用Oracle Objects for OLE中间件连接Oracle数据库的区别:

(1)ADO是通过微软公司提供的接口连接数据库服务器的,方法比较灵活;而使用Oracle Objects for OLE则是通过Oracle提供的SQL*NET来连接数据库服务器的,与数据库服务器的连接效率较好。

(2)使用ADO连接数据库后,可以对Oracle数据库进行基本操作;而使用能Oracle Objects for OLE则能更好地发挥Oracle数据库的特有功能,如STORED PROCEDURE、STORED FUNCTION、PACKAGE或Multiple Cursor等。

 

2.4  SQL语句操作数据库方案

在ASP应用程序中,不但可以通过ADO的Recordset对象操作数据库中的记录,还可以通过执行定义的SQL语句来操作数据库中的数据。SQL语言(Structured Query Language,结构化查询语言)是关系数据库的标准语言,用户使用基本的SQL语句可以在不同的数据库系统上执行同样的操作(读者可参考本章2.1.2节中常用SQL命令的介绍)。

在本节中,将介绍使用SQL Server的Transact-SQL和Oracle的PL/SQL操作数据库中的数据。Transact-SQL是结构化查询语言(SQL)的增强版本,它是面向Microsoft SQL Server的,使用Transact-SQL可以创建数据库或其他数据对象、创建数据库备份、从数据库中提取数据和修改数据、动态地改变SQL Server中的设置等,因此使用Transact-SQL极大地提高了应用程序的实用性;在Oracle数据库中使用的PL/SQL语言(Procedural Language/SQL,过程化SQL语言),是对标准SQL语言的扩展,它将变量、控制结构、过程、函数等结构化程序设计的要素引入到了SQL语言中,使其能够编制比较复杂的SQL程序。

2.4.1  单条数据操作

在实际应用中,通过在定义的SQL语句中使用适当的SQL命令并执行该SQL语句,可以向数据库中添加一条数据、修改数据库中的一条数据或者删除一条数据。

1.方案分析

在开发和管理网站过程中,用户通常要向数据库中追加数据,同时网站后台的管理人员还需要对数据库中的数据进行修改或者删除的操作。使用Insert、Update和Delete这3个SQL命令,可以分别实现添加、修改和删除数据的功能。使用SQL语句操作单条数据的流程图,如图2.28所示。

图2.28  使用SQL语句操作单条数据的流程图

2.实施过程

*  实例位置:光盘"mr"2"2.4"2.4.1

使用ASP开发数据库系统程序时,可以通过执行SQL语句来对数据库进行操作,不仅可以节省开发时间,又可以提高操作效率。下面以网站的文章中心模块为例,介绍向SQL Server数据库中添加一篇文章、修改数据库中的一篇文章和删除指定的一篇文章的实现过程,运行效果如图2.29所示。

图2.29  单条数据操作

在文章添加页面中,用户可以选择添加的文章类别,并填写文章的标题、作者、内容等信息,然后单击“添加”按钮,即可将此次填写的文章信息录入到数据库中。

为了便于读者理解,下面以列表的形式给出文章添加页面中所涉及到的表单及表单元素,如表2.18所示。

表2.18                        文章添加页面中的表单及表单元素

名称

元素类型

重要属性

含义

form1

form

method="post" action="code.asp"

添加信息表单

sel_class

select

<option selected>选择类别</option>
<%sqlstr="select id,Acname from tb_article_class"
   Set rs=conn.Execute(sqlstr)
   strA=rs.GetRows
   rs.close
   Set rs=Nothing
   For i=0 to ubound(strA,2) %>
<option value="<%=strA(0,i)%>"><%=strA(1,i)%></option>
<% Next %>

文章类别

txt_title

input

type="text" class="textbox" maxlength="100" title="文章标题"

文章标题

txt_author

input

type="text" class="textbox" maxlength="50" title="文章作者"

文章作者

txt_content

select

cols="45" rows="6" id="txt_content" title="文章内容" onKeyDown="CountStrByte(this.form.txt_content,this.form.total,
this.form.used,this.form.remain);" onKeyUp="CountStrByte
(this.form.txt_content,this.form.total,
this.form.used,this.form.remain);"

文章内容

total

input

type="text" disabled class="textbox" id="total"  value="500"

最多字节数

used

input

type="text" disabled class="textbox" id="used"  value="0"

已用字节数

remain

input

type="text" disabled class="textbox" id="remain" value="500"

剩余字节

flag

input

type="submit" id="flag" onClick="return Mycheck(this.form)"

添加按钮

Submit2

input

type="reset" name="Submit2"

重置按钮

*   注意:在上表的文章类别下拉列表框中,调用了Recordset对象的GetRows方法将文章类别信息表中的数据存储一个二维数组中,然后读取二维数组中内容显示在下拉列表框中。关于GetRows的介绍,请参见本章2.1.2节ADO编程技术。

当用户单击“添加”按钮后,程序处理页面首先获取表单中的数据信息、设置错误陷阱、定义含有Insert命令的SQL语句,然后调用Connection对象的Execute方法执行该SQL语句,并在执行过程中通过ObjectContext对象实现事务处理。关键代码如下:

例程2-19  代码位置:光盘"mr"2"2.4"2.4.1"code.asp

<%

flag=Request("flag")

If flag="添加" Then

sel_class=Str_filter(Request.Form("sel_class"))

   txt_title=Str_filter(Request.Form("txt_title"))

   txt_author=Str_filter(Request.Form("txt_author"))

   txt_content=Str_filter(Request.Form("txt_content"))

获取表单数据后,设置错误陷阱,并执行SQL语句。

   On Error Resume Next

   sqlstr="Insert into tb_article(Aclass,Atitle,Aauthor,Acontent) values("&sel_class&",'"&txt_title&"','"&txt_author&"','"&txt_content&"')"

   conn.Execute(sqlstr)

进行错误处理,如果执行过程中出现异常,则调用ObjectContext对象的SetAbort方法终止当前网页所启动的事务处理,并且将事务先前所做的处理撤消到初始状态,即为事务“回滚”;如果执行正常,则调用ObjectContext对象的SetComplete方法完成事务处理,即为事务“提交”。

  

   If err<>0 Then

       err.clear

       ObjectContext.SetAbort()

   End If

   ObjectContext.SetComplete()

脚本本身不能判断事务处理是成功还是失败,可以编写事务终止或者成功提交时调用的事件显式说明事务处理的情况。OnTransactionAbort事件是当事务处理失败时引发的事件,OnTransactionCommit事件是当事务处理成功时引发的事件。

   Sub OnTransactionCommit() '事务执行成功时触发的事件

     Response.Write("<script language='javascript'>alert('文章信息添加成功!');location.href='article_add.asp';</script>") 

      Response.Flush()

    End Sub

    Sub OnTransactionAbort()  '事务执行失败时触发的事件

      Response.Clear()

     Response.Write("<script language='javascript'>alert('出现异常错误!');history.go(-1);</script>")

      Response.Flush()

    End Sub

End IF

%>

用户添加文章信息后,可以在文章管理页面中查看文章信息。单击首页面中的“文章管理”超链接进入到文章管理页面,在该页面中提供了“修改”和“删除”文章信息的入口,运行效果如图2.30所示。

图2.30  文章管理页面

在文章管理页面中单击“修改”超链接,可以打开指定文章的修改页面,在该页面中可以编辑文章信息,例如:修改文章的类别、标题、作者和内容信息;然后单击“修改”按钮提交表单数据。

程序处理页面获取提交的表单数据,并设置错误陷阱,然后执行嵌有Update命令的SQL语句,实现修改单条数据的功能。关键代码如下:

例程2-20  代码位置:光盘"mr"2"2.4"2.4.1"code.asp

<%

If flag="修改" Then

id=Request.Form("id")

   sel_class=Str_filter(Request.Form("sel_class"))

   txt_title=Str_filter(Request.Form("txt_title"))

   txt_author=Str_filter(Request.Form("txt_author"))

   txt_content=Str_filter(Request.Form("txt_content"))

   On Error Resume Next

   sqlstr="update tb_article set Aclass="&sel_class&",Atitle='"&txt_title&"',Aauthor='"&txt_author&"',Acontent='"&txt_content&"' where id="&id&""

   conn.Execute(sqlstr)

   If err<>0 Then

       err.clear

       Response.Write("<script language='javascript'>alert('出现异常错误!');history.go(-1);</script>")

       Response.End()

   Else

       Response.Write("<script language='javascript'>alert('文章信息修改成功!');location.href='article_admin.asp';</script>")   

   End If

End If

%>

在文章管理页面中,单击“删除”按钮可以删除指定的文章信息。在程序处理页面中,首先获取指定文章的ID自动编号,然后执行嵌有Delete命令的SQL语句实现删除功能。代码如下:

例程2-21  代码位置:光盘"mr"2"2.4"2.4.1"code.asp

<%

If flag="删除" Then

   id=Request.Form("id")

   On Error Resume Next

   If id<>"" and Isnumeric(id) Then

     sqlstr="delete from tb_article where id="&id&""

     conn.Execute(sqlstr)

   End If

   If err<>0 Then

       Response.Write("<script language='javascript'>alert('出现异常错误!');history.go(-1);</script>")

       Response.End()

   Else

       Response.Write("<script language='javascript'>alert('文章信息删除成功!');location.href='article_admin.asp';</script>")   

   End If   

End If

%>

3.补充说明

在ASP应用程序中,可以使用Recordset对象的AddNew方法或者使用Insert Into语句将数据插入到数据库中。Recordset对象的AddNew方法相当于将Insert Into语句封装起来,在对具有多字段或者字段内容包含引号等数据进行操作时,使用AddNew方法添加数据可以减少编写程序出错的机会。当对大量数据进行操作时,直接使用Insert Into语句将会加快操作速度,因为减少了ADO的“翻译”时间。

另外,在操作SQL Server数据库时,在SQL语句中可以使用全局变量@@IDENTITY获取自动编号。@@IDENTITY是返回最后插入记录的标识值,返回类型为numeric。使用Insert Into语句添加记录后,可以通过创建Recordset对象并应用@@IDENTITY获取记录的自动编号,例如:

<%

str1=Request.Form("txt_name")

str2=Request.Form("txt_pwd")

str3=Request.Form("txt_tel")

sqlstr="insert into tb_user(UserName,Upwd,Utel) values('"&str1&"','"&str2&"','"&str3&"')"

conn.Execute(sqlstr)

Set rs=Server.CreateObject("ADODB.Recordset")

sqlstr="select @@IDENTITY as id"

rs.open sqlstr,conn,1,1

addid=rs("id")

rs.close

Set rs=Nothing

%>

2.4.2  批量操作数据

通过执行定义的SQL语句,并同时使用循环语句,可以批量地向数据库中添加数据、修改数据和删除数据,以提高操作数据库的效率。

1.方案分析

在开发大型数据库系统时,通常需要录入大量地数据,丰富网站内容以供浏览者浏览或参考。通过批量操作数据,既节省录入数据的时间又可方便快捷地对多条数据进行编辑。

在批量添加数据时,可以使用Session对象定义的动态二维数组暂时保存添加的多条数据,当用户确定数据信息无误后,再将动态二维数组中的数据添加到数据库中。在添加过程中,通过在For…Next循环语句中,执行Insert语句实现批量添加数据的功能。

在批量更新数据时,用户可以修改多条数据信息,然后通过循环语句调用Update语句实现批量修改数据的功能;也可以在Update语句中检索出符合条件的数据,执行该语句以实现批量修改数据的功能。

批量删除数据有多种方法,如:通过在Delete语句中检索出符合条件的数据进行删除、使用临时表进行删除或者使用truncate命令进行删除等。

通过SQL语句批量操作数据的过程,如图2.31所示。

图2.31  通过SQL语句批量操作数据

2.实施过程

*  实例位置:光盘"mr"2"2.4"2.4.2

对于一些需要实时更新或者频繁更新信息的网站,网站管理员的工作量也相对繁重,为了减轻工作量、节省操作时间并提高工作效率,可以通过SQL语句实现批量操作数据的功能。下面以新闻网站为例,介绍批量添加短讯信息、批量修改短讯信息和批量删除短讯信息的实现过程,运行效果如图2.32所示。

图2.32  批量操作数据

在新闻管理系统的首页面中,用户单击左侧菜单栏中的“短讯信息添加”超链接,在打开的页面中,填写短讯标题、选择短讯类别和短讯内容,然后单击“添加”按钮,填写的一条短讯信息会立即显示在页面中。可以继续填写各项内容,页面上则会显示对应个数的短讯信息。查看信息无误后,单击“确定入库”按钮,将所填写的多条数据添加到数据库中;如果信息存在错误,可以单击“清空内容”按钮,删除页面上显示的短讯信息。

当用户单击“添加”按扭后,程序处理页面将获取表单中的数据,并将其赋值给一维数组,此一维数组作为自定义函数(此函数的功能是将数据存储到动态数组中)的一个参数。当用户单击“确定入库”按钮后,在For…Next循环语句中执行Insert语句,将动态数组中的数据添加到指定数据库中。当用户单击“清空内容”按钮后,设置动态数组为空,从而清除页面中显示的短讯信息。关键代码如下:

例程2-22  代码位置:光盘"mr"2"2.4"2.4.2"news_add.asp

<%

Dim arr_data(10)

If Not Isempty(Request("add")) Then

  nums=Request.Form("txt_item").count

  On Error Resume Next

  For t=1 to nums

    arr_data(t-1)=Request.Form("txt_item")(t)

  Next 

  call addBatch(arr_data,nums)

  If err<>0 Then

    Response.Write("<script language='javascript'>alert('出现错误,请重新填写信息内容!');history.go(-1);</script>")

   Response.End()

  End If

End If

自定义一个名为addBatch的函数,实现的功能是通过定义动态数组存储添加的多条信息。在此函数中,判断如果是本次操作添加的第一条信息,则将其赋值给一个二维数组arr,然后将此二维数组赋予指定的Session变量;如果不是第一条信息,则将Session变量中的值取出赋予arr,并重新定义二维数组arr,使其一维最大下标增加1,然后将得到的新的数据添加到arr的一维最大下标处,将Session变量中原有的数据添加到二维数组arr的空位处,最后将最新的arr赋予Session变量。

Function addBatch(arr_pram,field_nums)

  if not isarray(session("arr_rk")) then  '如果是添加头条记录,所做的操作

       dim arr(0,10)

       For s=0 to field_nums-1

         arr(0,s)=arr_pram(s)

       Next

  else

       arr=session("arr_rk")

       UB=ubound(arr,1)+1

       redim arr(UB,field_nums-1)

       sessionarr=session("arr_rk")

       '保存新添加的信息

       For s=0 to field_nums-1

         arr(UB,s)=arr_pram(s)

       Next

       '应用循环将保存在seesion("arr_rk")动态数组中的信息添加到arr中

       For I = 0 To ubound(sessionarr,1)

          For J = 0 To field_nums-1

                arr(I, J)=sessionarr(I,J)

          Next

       Next  

   end if

   session("arr_rk")=arr

End Function

当用户单击“确定入库”按钮后,首先获取保存在动态数组(即Session 变量)中的信息,然后使用For…Next语句,并在其中执行Insert语句,逐条地将动态数组中的数据添加到数据库中,最后设置Session变量的值为空,结束本次操作。

If Not Isempty(Request("store")) Then

  if isarray(session("arr_rk")) then

    On Error Resume Next

   arr_news=session("arr_rk")

   For t=0 to ubound(arr_news,1)

     sqlstr="insert into tb_news(Title,NewsType,Content) values('"&arr_news(t,0)&"','"&arr_news(t,1)&"','"&arr_news(t,2)&"')"

     conn.Execute(sqlstr)

   Next

   session("arr_rk")=""

    If err<>0 Then

     Response.Write("<script language='javascript'>alert('出现异常错误!');history.go(-1);</script>")

     Response.End()

   Else

     Response.Write("<script language='javascript'>alert('批量添加记录成功!');location.href='news_add.asp';</script>")

   End If

  Else

    Response.Write("<script language='javascript'>alert('请填写信息后再进行入库操作!');history.go(-1);</script>")

   Response.End()

  end if 

End IF

*   注意:在执行添加操作后,一定要给Session变量赋予空值,否则将出现错误。Sesssion变量是具有有效时限的,超出时限则自动终止会话,默认值一般为20分钟。

当用户单击“清空内容”按钮后,页面将给Session变量赋予空值,即清空动态数组中的信息。

If Trim(Request.Form("del"))="清空内容" Then session("arr_rk")=""

%>

在新闻管理系统的首页面,用户通过单击左侧菜单栏中的“短讯信息管理”超链接,可以打开短讯信息管理页面,在该页面中用户可以批量审核短讯信息、删除指定的一条或多条短讯信息或者删除全部短讯信息,运行效果如图2.33所示。

图2.33  短讯信息管理页面

在页面中,用户可以通过勾选每一条数据前端对应的复选框,选择需要编辑的数据,也可以通过勾选“[全选/反选]”复选框选择本页中的全部数据。

用户单击“通过审核”按钮,可以更改数据的状态值,即由“未审核”转换为“已审核”。程序处理页面获取待编辑数据的ID自动编号,然后执行Update语句设置字段Nfocus的值为1,来实现批量修改数据的功能,代码如下:

例程2-23  代码位置:光盘"mr"2"2.4"2.4.2"code.asp

<%

If flag="通过审核" Then

  chk_id=Trim(Request.Form("chk_id")) '获取勾选的复选框的值

  If chk_id<>"" Then

    On Error Resume Next

    sqlstr="update tb_news set Nfocus=1 where id in ("&chk_id&")"

   conn.Execute(sqlstr)

   If err<>0 Then

      Response.Write("<script language='javascript'>alert('出现异常错误!');history.go(-1);</script>")

     Response.End()

    Else

      Response.Write("<script language='javascript'>alert('信息状态更新成功!');location.href='news_admin.asp';</script>")  

    End If

  Else

      Response.Write("<script language='javascript'>alert('请选择记录后再进行更新操作!');history.go(-1);</script>")

     Response.End()

  End If

End If

%>

在短讯信息管理页面中,单击“删除所选”按钮可以删除指定的数据。程序处理页面获取待编辑数据的ID自动编号,使用Select语句检索出编号不包含在指定的集合内的数据,将其存储在临时表#tb_del中,然后使用drop语句删除表tb_news,再使用Select…into语句将临时表#tb_del中的数据复制到新表tb_news中,以达到删除指定数据的目的。用户也可以使用Delete语句直接删除指定的数据。代码如下:

例程2-24  代码位置:光盘"mr"2"2.4"2.4.2"code.asp

<%

If flag="删除所选" Then

  chk_id=Trim(Request.Form("chk_id")) '获取勾选的复选框的值

  If chk_id<>"" Then

    On Error Resume Next

   '使用临时表删除记录

   sqlstr="select * into #tb_del from tb_news where id not in ("&chk_id&")"

    conn.Execute(sqlstr) '创建一个临时表存放保留的数据

    conn.Execute("drop table tb_news")'删除原表

    conn.Execute("select * into tb_news from #tb_del")'将临时表中的数据导入到新表中

    '也可以直接使用下面的delete语句

   'sqlstr="delete from tb_news where id in ("&chk_id&")"

   'conn.Execute(sqlstr)

   If err<>0 Then

      Response.Write("<script language='javascript'>alert('出现异常错误!');history.go(-1);</script>")

     Response.End()

    Else

      Response.Write("<script language='javascript'>alert('批量删除记录成功!');location.href='news_admin.asp';</script>")  

    End If

  Else

      Response.Write("<script language='javascript'>alert('请选择记录后再进行删除操作!');history.go(-1);</script>")

     Response.End()

  End If

End IF

%>

根据实际情况,有时需要删除数据表中的全部信息。在短讯信息管理页面中,单击“删除全部”按钮,将删除表tb_news中的全部数据信息。程序中是通过执行truncate table语句,来删除表中内容的,代码如下:

例程2-25  代码位置:光盘"mr"2"2.4"2.4.2"code.asp

<%

If flag="删除全部" Then

  On Error Resume Next  

  sqlstr="truncate table tb_news"

  conn.Execute(sqlstr) 

    If err<>0 Then

      Response.Write("<script language='javascript'>alert('出现异常错误!');history.go(-1);</script>")

     Response.End()

    Else

      Response.Write("<script language='javascript'>alert('已删除全部记录!');location.href='news_admin.asp';</script>")  

    End If

End If

%>

*   注意:truncate table语句的执行过程不会记录于事务日志文件中,因此速度较快,但删除后就无法利用事务日志文件进行恢复了。如果有其他数据表与待删除的数据表存在关联,则无法使用truncate table语句进行删除操作。

3.补充说明

在数据库设计过程中,需要为数据表设置主键,使其中的记录具有唯一标识。为了更好地对数据库中的数据进行操作,拓展对数据库操作的认识,下面介绍3种常见的设置主键方法:数字变化(如数字自动加1)方法、GUID方法和自定义方法。

(1)数字变化

在Access和SQL Server数据库中,都可以通过设置字段属性来设置主键,使该字段的数值自动加1。例如,在Access数据表中选择数据类型为“自动编号”,如图2.34所示;在SQL Server数据表选择数据类型为“int”,并将字段属性“标识”选择为“是”,如图2.35所示。

图2.34  在Access数据表中设置主键

图2.35  在SQL Server数据表中设置主键

(2)GUID方式

GUID是Globally Unique Identifier的缩写,是由一个特殊算法产生的一个二进制长度为128位的字符串,在空间上和时间上都具有唯一性。它可以应用于注册表、类及接口标识、自动生成的机器名或者目录等;还可以应用在数据库方面,例如,将其生成的GUID作为数据表的主键值。在SQL Server数据库中可以使用NewID()语句生成GUID。

GUID是随机生成并且是无顺序的,不容易记忆和输入,还可能造成存储空间增大或者索引时间较慢,所以使用GUID时应慎重。

(3)自定义方式

自定义方式是指根据实际需要,应用适合的算法编写程序确定主键值。此方式应用灵活,一般在相对比较小的内部项目中使用。

2.4.3  通过存储过程操作数据

在实际编程中,一些代码段是可以重复使用的,为了增强代码的重用性,可以在数据库中(例如:SQL Server数据库、Oracle数据库等)创建存储过程,然后在ASP程序中调用该存储过程即可。存储过程中的内容是经编译后存储在数据库中的,所以执行的时候不必再次进行编译,从而提高了程序的执行效率。

1.方案分析

在开发项目时,考虑到程序的运行效率以及程序的可读性,程序人员会将实现一些特殊功能的或经常用到的功能写成存储过程,在需要时通过指定存储过程的名字或者给出相应参数来调用执行它即可。在实际操作时,根据数据库的不同,编写存储过程时使用的SQL语言也不尽相同,例如,在SQL Server数据库中使用Transact-SQL语言,在Oracle数据库中使用PL/SQL等。

在ASP程序中调用存储过程操作数据的流程图,如图2.36所示。

图2.36  存储过程操作数据的流程图

2.实施过程

使用存储过程,可以方便快捷地向数据库中添加数据、修改数据库中的数据、将数据进行分页显示、删除指定的数据等。根据数据库的不同,下面分别介绍调用SQL Server中的存储过程和调用Oracle中的存储过程操作数据的具体实施过程。

l          调用SQL Server中的存储过程操作数据

*  实例位置:光盘"mr"2"2.4"2.4.3"01

在SQL Server数据库中,可以通过创建存储过程来操作数据。下面以图书管理系统为例,介绍调用存储过程向数据库中添加图书信息、修改图书信息、删除图书信息以及使用存储过程实现分页显示图书信息的实现过程,运行效果如图2.37所示。

图2.37  调用SQL Server中的存储过程操作数据

在设计数据库结构时,分别创建图书信息表tb_book、图书作者信息表tb_author和出版社信息表tb_pub。

为了方便读者理解,下面列出在SQL Server数据库中创建的存储过程(关于存储过程的介绍,请参见本章2.1.2节存储过程与触发器),并介绍其实现的功能。

²         存储过程book_add

在SQL Server数据库中创建名为book_add的存储过程,该存储过程实现的功能是将数据添加到数据表tb_book中,并同时更新表tb_author中作者的出书数量值。在创建存储过程时,定义了与字段及字段类型相对应的参数信息,在SQL语句中使用Insert命令将传递的数据信息添加到数据表中,接下来使用update命令更新表tb_author中指定记录的字段值。代码如下:

CREATE PROCEDURE book_add  @book_id char(40),@book_name varchar(50),

@book_type varchar(50),@au_id char(40),@pub_id char(40),@price money,@pub_date smalldatetime,

@advance_money money,@sales int  AS

 insert into tb_book(book_id,book_name,book_type,au_id,pub_id,price,pub_date,advance_money,sales) values(@book_id,@book_name,@book_type,@au_id,@pub_id,@price,@pub_date,@advance_money,@sales)

 update tb_author set book_nums=book_nums+1 where au_id=@au_id

²         存储过程book_read

在SQL Server数据库中创建名为book_read的存储过程,该存储过程实现的功能是统计符合条件的显示在页面中的图书记录总数。在创建存储过程时,定义两个参数,一个参数传递数据表名称,一个参数传递与图书名称模糊匹配的字符串信息,然后在select语句中使用count聚集函数统计记录总数。代码如下:

CREATE PROCEDURE book_read @tablename varchar(50),@book_name varchar(50)='%'  AS

if @tablename='tb_book'

select count(*) from tb_book where book_name like '%'+@book_name+'%'

²         存储过程book_page

在SQL Server数据库中创建名为book_page的存储过程,该存储过程实现的功能是使符合条件的记录分页显示在页面中,创建的具体步骤如下:

(1)定义4个参数,分别表示数据表名称、模糊匹配的图书名称字符串信息、每页显示的记录数和当前页码数。

(2)根据每页显示的记录数和当前页码数计算出当前的记录指针所在的位置。

(3)在执行过程中,需要使用表temp07(表名称可以修改),所以先判断数据库中是否已存在该表,如果存在则使用drop命令删除该表。

(4)使用select…into语句将符合条件的记录复制到表temp07中,并在此select语句的前后使用set nocount on语句和set nocount off语句,使用这两个语句的含义是在执行select语句时不返回记录集。

(5)使用set rowcount语句的作用是在返回指定的行数之后停止处理查询,即只检索出指定条数的记录信息。

(6)定义SQL语句,根据表temp07、表tb_author和表tb_pub之间的关联,检索出3个表中需要显示的字段信息,并且根据当前的记录指针所在的位置,检索出指定数量的未显示在页面中的记录信息。

(7)最后使用Execute命令执行SQL语句,并删除表temp07(以方便下次操作),实现分页显示数据的功能。创建代码如下:

CREATE PROCEDURE book_page @tablename varchar(50),@book_name varchar(50)='%',@pagesize int,@page int AS

if @tablename='tb_book'

begin

declare @saw int

declare @sqlstr varchar(200)

set @saw=@pagesize*(@page-1)

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[temp07]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)

drop table temp07

begin

set nocount on

select * into temp07 from tb_book where book_name like '%'+@book_name+'%' order by add_date desc

set nocount off

set rowcount @pagesize

set @sqlstr='select a.author,b.*,p.pub_name from tb_author a inner join temp07 b on a.au_id=b.au_id inner join tb_pub p on b.pub_id=p.pub_id where b.book_id not in (select top '+str(@saw)+' book_id from temp07)'

execute(@sqlstr)

drop table temp07

end

end

*   注意:在存储过程中,可能有多条SQL语句,对于一些不需要返回实际数据的语句,在执行前后可以使用set nocount on|off语句,该设置大量减少了网络流量,可显著提高程序性能。

²         存储过程book_edit

在SQL Server数据库中创建名为book_edit的存储过程,该存储过程实现的功能是更新数据库中指定的记录信息。在创建存储过程时,定义了与字段及字段类型相对应的参数信息,以及待修改记录的图书编号的参数信息,然后在SQL语句中使用Update命令更新该记录信息。代码如下:

CREATE PROCEDURE book_edit @editid char(40),@book_id char(40),@book_name varchar(50),

@book_type varchar(50),@au_id char(40),@pub_id char(40),@price money,@pub_date smalldatetime,

@advance_money money,@sales int AS

update tb_book set

book_id=@book_id,book_name=@book_name,book_type=@book_type,au_id=@au_id,pub_id=@pub_id,price=@price,pub_date=@pub_date,advance_money=@advance_money,sales=@sales

where book_id=@editid

GO

²         存储过程book_del

在SQL Server数据库中创建名为book_del的存储过程,该存储过程实现的功能是批量删除数据库中的记录信息,其创建的具体步骤如下:

(1)定义两个参数,一个参数@tablename传递数据表名称,另一个参数@delid传递待删除的一条或多条记录的图书书号的字符串。

(2)在while语句中定义循环的条件,以逐条地删除指定的记录。

(3)在图书书号的字符串中,书号之间是以逗号“,”分隔的,在while循环语句的主体部分,首先使用charindex函数逐个的找出逗号“,”所在的位置,然后根据位置的不同,在substring函数中使用不同的参数值,提取出具体的图书书号,再根据书号调用delete命令删除此图书信息。

(4)在while循环语句中,是通过判断逗号“,”来确定书号的,图书书号字符串的末端没有逗号“,”,那么就判断不出最后一个图书书号,所以要另外使用substring函数并给定参数提取最后一个图书书号。当记录集中只有一条记录时,在while语句中也判断不出图书书号,所以定义一个变量@count_i(初始值为1),在while语句中根据情况改变其值,如果该变量值未发生变化则说明记录集中只有一条待编辑的记录,这时直接调用delete命令删除图书信息即可。

CREATE PROCEDURE book_del @tablename varchar(50),@delid nvarchar(300) AS

set nocount on

if @tablename='tb_book'

declare @PointerPrev int

declare @PointerCurr int

declare @count_i int

declare @delid_split char(40)

set @PointerPrev=1

set @count_i=1

while (@PointerPrev < len(@delid))

begin

  set @PointerCurr=charindex(',',@delid,@PointerPrev)

  if (@PointerCurr > 0)

  begin

    if (@PointerPrev=1)

    set @delid_split=substring(@delid,@PointerPrev,@PointerCurr-@PointerPrev)

    else

    set @delid_split=substring(@delid,@PointerPrev+1,@PointerCurr-@PointerPrev-1)

    delete from tb_book where book_id=@delid_split

    set @PointerPrev=@PointerCurr+1

    set @count_i=@count_i+1

  end

  else     

    break    

end

if (@count_i = 1)    

set @delid_split=@delid

else  

set @delid_split=substring(@delid,@PointerPrev+1,len(@delid)-@PointerPrev)

delete from tb_book where book_id=@delid_split

set nocount off

*   注意:复选框可用来存储图书书号,即获取到的复选框的值是保存多个图书书号的字符串,并且书号间以逗号“,”分隔的,但是逗号“,”与下一个书号之间是有一个空格的,例如:"1, 7, 10, 22"。

以上给出了存储过程的创建代码,下面介绍在程序页面中是如何调用存储过程实现具体功能的。

在图书档案管理页面中,单击“图书添加”超链接,打开图书信息添加页面。在设计图书信息添加页面时,分别读取表tb_author和表tb_pub中的信息到“作者”下拉列表框和“出版社”下拉列表框中。当用户单击“添加”按钮时,页面会通过JavaScript脚本程序在客户端判断输入的信息是否符合要求,例如:判断在“价格”和“预计销售金额”文本框中输入的是否为货币类型的数据、在“出版日期”文本框中输入的字符串是否符合日期格式等。

*   注意:在将数据表中的信息读取到下拉列表框时,首先调用了Recordset对象的GetRows方法将数据信息存储在一个二维数组中,然后再读取二维数组中的数据到下拉列表框中。

用户填写图书信息,并单击“添加”按钮后,程序处理页面首先判断添加的信息是否与数据库中已有的数据信息发生冲突,如果出现重复信息的情况则提示用户重新填写图书内容,如果未出现重复信息的情况,则创建一个Command对象的实例,通过设置Command对象的属性和调用其指定的方法,定义以下所要执行的是一个存储过程,并定义参数、将从表单中获取的数据赋值给参数,然后执行Command对象的Execute方法执行此操作,从而完成添加数据的功能。关键代码如下:

例程2-26 代码位置:光盘"mr"2"2.4"2.4.3"01"code.asp

<%

flag=Trim(Request("flag"))

If flag="添加"  Then

    On Error Resume Next

   Set rsf=conn.Execute("select book_id from tb_book where book_id='"&Trim(Request.Form("txt_item")(1))&"'")

   If Not (rsf.eof and rsf.bof) Then

     Response.Write("<script language='javascript'>alert('数据库中已存在该记录,请重新输入!');history.go(-1);</script>")

     Response.End()

   Else

创建Command对象,设置其CommadText属性值为“book_add”,CommandTyep属性值为“adCmdStoredProc”(表明所指定的CommandText参数是一个存储过程的名称)。

*   注意:在code.asp文件的开始处,使用#include指令包含了文件adovbs.inc,在该文件中定义了ADO的具体参数值,如:adCmdStoredProc、adChar、adParamInput等。

   Set cmd=Server.CreateObject("ADODB.Command")

   cmd.ActiveConnection=conn

   cmd.CommandText="book_add"

   cmd.CommandType=adCmdStoredProc 

调用Command对象的CreateParameter方法创建新的Parameter对象,然后调用Append方法将此创建的Parameter对象增加到Command对象的Parameters数据集合中,并对其进行赋值。重复以上步骤,一直到设置了所有存储过程需要的参数。

*   注意:在调用Command对象的CreateParameter方法时,请选择正确的参数类型,如:adVarChar、adCurrency或adDBDate等,否则程序将出现错误。

   set param=cmd.CreateParameter("@book_id",adChar,adParamInput,40)

   cmd.Parameters.Append param

   cmd.Parameters("@book_id")=Trim(Request.Form("txt_item")(1))

   set param=cmd.CreateParameter("@book_name",adVarChar,adParamInput,50)

   cmd.Parameters.Append param

   cmd.Parameters("@book_name")=Trim(Request.Form("txt_item")(2))

   set param=cmd.CreateParameter("@book_type",adVarChar,adParamInput,50)

   cmd.Parameters.Append param

   cmd.Parameters("@book_type")=Trim(Request.Form("txt_item")(3))

   set param=cmd.CreateParameter("@au_id",adChar,adParamInput,40)

   cmd.Parameters.Append param

   cmd.Parameters("@au_id")=Trim(Request.Form("txt_item")(4))

   set param=cmd.CreateParameter("@pub_id",adChar,adParamInput,40)

   cmd.Parameters.Append param

   cmd.Parameters("@pub_id")=Trim(Request.Form("txt_item")(5))

   set param=cmd.CreateParameter("@price",adCurrency,adParamInput)

   cmd.Parameters.Append param

   cmd.Parameters("@price")=Trim(Request.Form("txt_item")(6))

   set param=cmd.CreateParameter("@pub_date",adDBDate,adParamInput)

   cmd.Parameters.Append param

   cmd.Parameters("@pub_date")=Trim(Request.Form("txt_item")(7))

   set param=cmd.CreateParameter("@advance_money",adCurrency,adParamInput)

   cmd.Parameters.Append param

   cmd.Parameters("@advance_money")=Trim(Request.Form("txt_item")(8))

   set param=cmd.CreateParameter("@sales",adInteger,adParamInput)

   cmd.Parameters.Append param

   cmd.Parameters("@sales")=Trim(Request.Form("txt_item")(9))

定义了所需的参数并进行赋值后,调用Command对象的Execute方法调用存储过程,执行添加数据的操作,并通过设置的错误陷阱返回执行的结果。

   cmd.Execute()

    If err<>0 Then

     err.clear

      Response.Write("<script language='javascript'>alert('出现异常错误!');history.go(-1);</script>")

     Response.End()

    Else

      Response.Write("<script language='javascript'>alert('通过存储过程添加记录成功!');location.href='book_add.asp';</script>")

    End If

   End If

   Set cmd=Nothing

   rsf.close

   Set rsf=Nothing

End If

%>

在图书档案管理页面中,单击“图书管理”超链接,打开图书信息管理页面。在该页面中,用户可以浏览图书信息、单击图书名称可以查看该图书的详细信息并可以进行修改操作、可以通过输入图书名称关键字查询图书信息以及通过选择记录项进行批量删除的操作,运行结果如图2.38所示。

图2.38  图书信息管理页面

在图书信息管理页面中,顺次调用存储过程book_read和book_page,实现分页显示数据的功能:首先获取图书名称文本框的值,以确定查询的字符串,然后调用存储过程book_read获取显示的记录总数,并根据定义的每页显示记录数计算出显示的页码数值,再获取当前页数值,最后调用存储过程book_page(该存储过程需要的参数请参见以上对创建代码的介绍)并返回一个记录集。关键代码如下:

例程2-27  代码位置:光盘"mr"2"2.4"2.4.3"01"book_admin.asp

<%

keyword=Trim(Request("keyword")) '获取图书名称文本框中的值

set rsn=conn.execute("book_read 'tb_book','"&keyword&"'")'调用存储过程book_read,获取符合条件的记录总数

page_size=6  '定义每页显示的记录数

'计算页码数

page_count=CInt(rsn(0)"page_size) 

If(CInt(rsn(0)) mod page_size)=0 then

  page_count=page_count

Else

  page_count=page_count+1

End if

'获得当前页数,注意pages的取值要保证为正数

pages=clng(Request("pages")) 

If pages="" or pages<1 then

  pages=1

Elseif pages>page_count Then

  pages=page_count

End If

'调用存储过程book_page,从数据库中读取指定数量的记录显示在页面中

set rs=conn.execute("book_page 'tb_book','"&keyword&"','"&Cint(page_size)&"','"&Cint(pages)&"'")

if rs.eof or rs.bof then

  Response.Write("<tr align='center' bgcolor=#FFFFFF><td colspan='7' height=22>暂时无图书信息!<td><tr>") 

  Response.End()

else

  n=0

  Do while not rs.eof

%>

  <tr bgcolor="#FFFFFF">

    <td align="center"><input name="chk_id" type="checkbox" id="chk_id" value="<%=Trim(rs("book_id"))%>"></td>

    <td height="22" align="center"><%=rs("book_id")%></td>

    <td height="22" align="center"><a href="book_modify.asp?book_id=<%=rs("book_id")%>" style="text-decoration:underline; color:#0099FF "><%=rs("book_name")%></a></td>

    <td height="22" align="center"><%=rs("author")%></td>

    <td height="22" align="center"><%=rs("pub_name")%></td>

    <td height="22" align="center"><%=rs("price")%></td>

    <td height="22" align="center"><%=rs("book_type")%></td>

  </tr> 

<%n=n+1

  rs.movenext

  loop

End If

%>

<!-- 略,设置分页的超链接,请详见源程序-->

在图书信息管理页面中,单击图书名称,可以打开图书信息修改页面,在该页面中可以修改图书的书号、名称、类型、作者、出版社等信息,当用户单击“修改”按钮提交修改信息后,程序处理页面将获取表单中的数据,并调用存储过程book_edit更新数据库的数据。其程序处理过程与图书信息添加过程相似,读者可参考以上对添加数据过程的介绍(详细代码请参见光盘给出的源代码)。

在图书信息管理页面中,通过勾选记录项前端的复选框或者勾选“[全选/反选]”复选框选择记录,然后单击“删除所选”按钮可以批量地删除数据库中的数据。在程序处理页面中,首先获取表单中复选框的值(该值是记录图书书号的字符串,并且书号之间是以逗号“,”分隔的),然后调用存储过程book_del实现批量删除数据的功能。代码如下:

例程2-28  代码位置:光盘"mr"2"2.4"2.4.3"01"code.asp

<%

If flag="删除所选" Then  

  chk_id=Trim(Request.Form("chk_id"))

  If chk_id<>"" Then   

    Set rs=conn.Execute("book_del 'tb_book','"&chk_id&"'")  

    Response.Write("<script language='javascript'>alert('通过存储过程删除记录成功!');location.href='book_admin.asp';</script>")  

  Else

    Response.Write("<script language='javascript'>alert('请选择项目后,再进行操作!');history.go(-1);</script>")

    Response.End()

  End If

End IF

%>

l          调用Oracle中的存储过程操作数据

*  实例位置:光盘"mr"2"2.4"2.4.3"02

在Oracle数据库中,可以通过创建存储过程来操作数据。下面以企业进销存管理系统为例,介绍调用存储过程添加员工信息、根据标准统计员工的销售业绩的实现过程,运行结果如图2.39所示。

图2.39  调用Oracle中的存储过程操作数据

在设计Oracle数据库结构时,分别创建员工信息表EMPLOYEE和员工销售业绩信息表RESULT。打开Oracle企业管理器或者是使用SQL PLUS工具可以创建存储过程,创建时使用的SQL语言是PL/SQL,下面列出在Oracle数据库中创建的存储过程,并介绍其实现的功能。

²         存储过程EMP_INFO

存储过程EMP_INFO的功能是将员工信息添加到数据表EMPLOYEE中。在创建时,定义与字段类型相匹配的输入参数,然后使用Insert Into命令实现添加功能。代码如下:

create or replace procedure scott.emp_info(

tempid in scott.employee.em_id%type,

tempname in scott.employee.em_name%type,

tempdept in scott.employee.dept%type,

tempjan in scott.employee.m_jan%type,

tempfeb in scott.employee.m_feb%type,

tempmar in scott.employee.m_mar%type)as

begin

  insert into scott.employee(em_id,em_name,dept,m_jan,m_feb,m_mar) values(

  tempid,tempname,tempdept,tempjan,tempfeb,tempmar);

end;

²         存储过程EMP_PROCESS

存储过程EMP_PROCESS的功能是根据员工信息表EMPLOYEE中记录的员工每月销售业绩和提供的评定标准,统计员工每月销售情况和总的销售情况,并将统计结果添加到数据表RESULT中。创建过程的具体步骤如下:

(1)定义与数据表EMPLOYEE和数据表RESULT中指定字段类型相匹配的输入参数。

(2)使用%rowtype定义一个获得整个记录数据类型的变量;使用%type定义一个与指定字段数据类型一致的变量;定义一个字符串变量;定义一个查询数据表EMPLOYEE中全部数据的游标;定义异常处理。

(3)打开游标,然后判断游标返回的记录集中是否有数据,如果没有数据,则触发异常处理。

(4)清空数据表RESULT中的数据。

(5)使用loop…end loop循环语句,在循环主体中首先提取游标中的数据赋给变量emprecord,然后计算员工3个月销售业绩的总和并赋给指定变量,并将员工每月销售业绩、业绩总和与提供的参数进行比较,如果员工的各项业绩均大于或等于标准数值,则将员工评定为“优秀”员工,否则评定为“落选”员工。

(6)通过Insert Into语句将员工的基本信息、业绩总和、评定结果插入到数据表RESULT中。

(7)关闭游标;调用commit命令;定义异常处理具体语句。

create or replace procedure emp_process(

tempjan in scott.employee.m_jan%type,

tempfeb in scott.employee.m_feb%type,

tempmar in scott.employee.m_mar%type,

temptotal in scott.result.TURNOVER%type)as

emprecord scott.employee%rowtype;

emptotal scott.result.turnover%type;

empflag varchar2(4);

cursor empcursor is

  select * from scott.employee;

errormsg exception;

begin

  open empcursor;

  if empcursor%notfound then

   raise errormsg;

  end if;

  delete from result;

  loop

   fetch empcursor into emprecord;

   emptotal:=emprecord.m_jan+emprecord.m_feb+emprecord.m_mar;

   if (emprecord.m_jan>=tempjan and

       emprecord.m_feb>=tempfeb and

       emprecord.m_mar>=tempmar and

       emptotal>=temptotal) then

       empflag:='优秀';

   else

       empflag:='落选';

   end if;

  exit when empcursor%notfound; 

  insert into scott.result(em_id,em_name,dept,m_jan,m_feb,m_mar,turnover,flag) values(emprecord.em_id,emprecord.em_name,emprecord.dept,emprecord.m_jan,emprecord.m_feb,emprecord.m_mar,emptotal,empflag);

  end loop;

  close empcursor;

  commit;

  exception

  when errormsg then

   dbms_output.put_line('无法打开数据库表employee');

end;

以上给出了Oracle存储过程的创建代码,下面介绍在程序页面中是如何调用过程实现具体功能的。

当用户单击页面左侧菜单栏中“员工信息添加”超链接后,用户可以在页面中填写员工的基本信息,例如:员工编号、员工姓名、所属部门、一月份业绩、二月份业绩和三月份业绩,然后单击“添加”按钮提交表单数据。

程序处理页面接收用户提交的数据,首先调用Command对象的Execute方法执行select语句判断数据表中是否存在重复记录,如果不存在重复记录,则创建Command对象实例,并设置其ActiveConnection属性为conn(定义的连接数据库的Connection对象实例)、CommandText属性为emp_info(存储过程名称)、CommandType属性(此属性值的定义请参考Include/adovbs.inc文件中的数据信息),然后根据存储过程所需参数,创建新的Parameter对象并给对象进行赋值,最后调用Command对象的Execute方法执行本次操作。关键代码如下:

例程2-29  代码位置:光盘"mr"2"2.4"2.4.3"02"code.asp

<%

flag=Trim(Request("flag"))

If flag="添加" Then

    On Error Resume Next

   Set rsf=conn.Execute("select em_id from employee where em_id='"&Trim(Request.Form("txt_item")(1))&"'")

   If Not (rsf.eof and rsf.bof) Then

     Response.Write("<script language='javascript'>alert('数据库中已存在该记录,请重新输入!');history.go(-1);</script>")

     Response.End()

   Else

   Set cmd=Server.CreateObject("ADODB.Command")

   cmd.ActiveConnection=conn

   cmd.CommandText="emp_info"

   cmd.CommandType=adCmdStoredProc

  

   set param=cmd.CreateParameter("@em_id",adInteger,adParamInput)

   cmd.Parameters.Append param

   cmd.Parameters("@em_id")=Trim(Request.Form("txt_item")(1))

  

   set param=cmd.CreateParameter("@em_name",adVarChar,adParamInput,10)

   cmd.Parameters.Append param

   cmd.Parameters("@em_name")=Trim(Request.Form("txt_item")(2))

   set param=cmd.CreateParameter("@dept",adVarChar,adParamInput,10)

   cmd.Parameters.Append param

   cmd.Parameters("@dept")=Trim(Request.Form("txt_item")(3))   

   set param=cmd.CreateParameter("@jan",adCurrency,adParamInput,10)

   cmd.Parameters.Append param

   cmd.Parameters("@jan")=Trim(Request.Form("txt_item")(4))

   set param=cmd.CreateParameter("@feb",adCurrency,adParamInput,10)

   cmd.Parameters.Append param

   cmd.Parameters("@feb")=Trim(Request.Form("txt_item")(5))

   set param=cmd.CreateParameter("@mar",adCurrency,adParamInput,10)

   cmd.Parameters.Append param

   cmd.Parameters("@mar")=Trim(Request.Form("txt_item")(6))

  

   cmd.Execute()

    If err<>0 Then

     err.clear

      Response.Write("<script language='javascript'>alert('出现异常错误!');history.go(-1);</script>")

     Response.End()

    Else

      Response.Write("<script language='javascript'>alert('通过过程向Oracle数据库中添加记录成功!');location.href='emp_add.asp';</script>")  

    End If

   End If

   Set cmd=Nothing

   rsf.close

   Set rsf=Nothing

End  If

%>

*   注意:在调用Command对象的CreateParameter方法创建Parameter对象时,应根据数据表中字段的数据类型,给出匹配的参数类型并给出相应的数据长度值,否则程序出错。

用户单击页面左侧菜单栏中“员工销售业绩统计”超链接,则可以在页面中根据文本框的提示填写员工每月标准销售金额和月总标准销售金额,然后单击“统计”按钮,在下方则显示出员工的销售业绩以及评定结果,运行效果如图2.40所示。

图2.40  员工销售业绩统计页面

当用户填写销售金额标准,并单击“统计”按钮后,程序处理页面将调用存储过程emp_process,并将获取到的表单数据作为存储过程的参数值,来实现统计员工销售业绩的功能。调用存储过程emp_process的程序代码与调用存储过程emp_info的程序代码相似,读者可以参考以上对调用存储过程emp_info的介绍。

在存储过程emp_process中,是将员工销售业绩的统计结果放置到数据表RESULT中的,说明页面中显示的统计报表是从数据表RESULT中读取的数据。在员工销售业绩统计页面中,首先创建一个OraSession对象,并调用其dbopendatabase方法建立与Oracle数据库的连接,同时创建一个Oradatabase对象,然后定义SQL语句、调用Oradatabase对象的dbcreatedynaset方法生成一个查询数据表RESULT中所有数据的记录集Oradynaset对象,再通过oradynaset对象的fields数据集合读取数据库表中对应的字段值。其关键代码如下:

例程2-30  代码位置:光盘"mr"2"2.4"2.4.3"02"emp_total.asp

<%

  set OraSession=Server.createobject("OracleInprocServer.XOraSession")

  set Oradatabase=OraSession.dbopendatabase("dbase","scott/tiger",0)

  sql="select * from result"

  set oradynaset=oradatabase.dbcreatedynaset(sql,0)

  do until oradynaset.eof

%>

  <tr align="center" bgcolor="#FFFFFF">

    <td height="22"><%=oradynaset.fields(0).value%></td>

    <td height="22"><%=oradynaset.fields(1).value%></td>

    <td height="22"><%=oradynaset.fields(2).value%></td>

    <td height="22"><%=oradynaset.fields(3).value%></td>

    <td height="22"><%=oradynaset.fields(4).value%></td>

    <td height="22"><%=oradynaset.fields(5).value%></td>

    <td height="22"><%=oradynaset.fields(6).value%></td>

    <td height="22"><%=oradynaset.fields(7).value%></td>

  </tr>

<%

  oradynaset.dbmovenext

  loop

%>

3.补充说明

通过ADO的内置对象Command对象和RecordSet对象都可以对数据库进行操作。Command对象的主要功能是向Web数据库传送数据查询的请求,RecordSet对象的主要功能是创建数据查询的结果集。

通过Command对象可以直接调用SQL语句,所执行的操作是在数据库服务器中进行的,提高了执行效率;它还可以在服务器端执行创建的存储过程,使ASP代码与数据库操作分开,便于维护并降低了网络流通量。

Recordset对象要求数据库返回符合条件的所有数据,当数据量很大时可能会造成网络阻塞或者数据库负荷过重的现象,程序整体的执行效率会降低。不过使用Recordset对象操作数据库的代码更易阅读和理解。

通过这两个对象都可以调用数据库中的存储过程。下面以SQL Server数据库为例,介绍如何通过Command对象和Recordset对象调用其中的存储过程。

通过Command对象调用存储过程的代码如下:

<%

'其中User_proc表示为存储过程的名称

Dim Conn,Connstr

Set Conn=Server.CreateObject("ADODB.Connection")

Connstr="provider=sqloledb;data source=(local);initial catalog=db_02;user id=sa;password=;"

Conn.Open Connstr '建立连接

Const adCmdStoredProc = &H0004

Set cmd=Server.CreateObject("ADODB.Command")

cmd.ActiveConnection=Conn

cmd.CommandText="User_proc"

cmd.CommandType=adCmdStoredProc

Set rs=cmd.Execute

%>

通过Recordset对象调用存储过程的代码如下:

<%

'其中User_proc表示为存储过程的名称

Dim Conn,Connstr

Set Conn=Server.CreateObject("ADODB.Connection")

Connstr="provider=sqloledb;data source=(local);initial catalog=db_02;user id=sa;password=;"

Conn.Open Connstr '建立连接

Const adCmdStoredProc = &H0004

Set rs=Server.CreateObject("ADODB.Recordset")

rs.ActiveConnection=conn

rs.open "User_proc",,,adCmdStoredProc

%>

2.4.4  通过触发器操作数据

触发器是一种特殊的存储过程,它针对数据表进行操作。当数据表进行更新操作时,在触发器内指定的操作就会自动执行。下面以SQL Server数据库为例,介绍通过触发器如何操作数据库中的数据。

1.方案分析

在开发动态网站时,经常会遇到操作互相关联的多个数据表的情况。例如,在电子商务网站中,会员注册的同时也将此会员的一些基本信息添加到另一个数据表中,以作为此会员登录网站的记录;或者在删除数据库中数据的同时,将此删除操作记录到另一个数据表中,以记载对数据库的操作信息。这时,为了保证数据的完整性,可以创建触发器来操作数据。

SQL Server触发器只为数据表上的Insert、Update和Delete事件所触发(关于触发器的介绍,读者可参见本章2.1.2节存储过程与触发器)。通过触发器操作SQL Server数据库中数据的流程图,如图2.41所示。

图2.41  通过触发器操作数据的流程图

2.实施过程

*  实例位置:光盘"mr"2"2.4"2.4.4

针对数据表可以创建触发器,通过触发器中定义的动作对该表或者相关联的数据表中的数据进行操作,不仅可以减少书写的程序代码段而且可以提高执行效率。下面以企业网站为例,为数据库中的管理员信息表创建3个触发器,分别在添加、修改、删除管理员信息的同时针对管理员登录信息表执行添加、修改和删除操作。运行效果如图2.42所示。

图2.42  通过触发器操作数据

在企业网站的首页面中,用户单击左侧菜单栏中的“管理员信息添加”超链接,在打开的页面中填写管理员名称、登录系统密码以及选择管理员级别,然后单击“添加”按钮,即可将填写的信息插入到管理员信息表tb_manager中,同时将此条记录中的ID自动编号和管理员名称信息添加到管理员登录信息表tb_login中。

当程序处理页面接收到“添加信息”表单中的数据后,先查询SQL Server数据库系统表sysobjects中是否存在名为add_tr的触发器,如果存在则使用drop trigger语句删除触发器;然后使用create trigger语句为数据表tb_manager创建after Insert触发器,在触发器中查询Inserted逻辑表并使用Insert Into语句向数据表tb_login中追加信息;再调用Connection对象的Execute方法执行Insert Into语句将表单中的数据插入到数据表tb_manager中。关键代码如下:

例程2-31  代码位置:光盘"mr"2"2.4"2.4.4"code.asp

<%

If flag="添加" Then

   On Error Resume Next

   '在创建触发器前,先删除同名触发器

   str="select name from sysobjects where name='add_tr' and type='tr'"

   Set rs=conn.Execute(str)

   If Not rs.eof Then conn.Execute("drop trigger add_tr")

   rs.close

   Set rs=Nothing  

   '创建触发器,并触发该触发器向另一数据表中添加数据

   str="create trigger add_tr on tb_manager after insert as insert into tb_login(Mid,Manager) select id,Mname from inserted"

   conn.Execute(str)   

   '执行添加操作

   Set rs=conn.Execute("select Mname from tb_manager where Mname='"&Trim(Request.Form("txt_item")(1))&"'")

   If Not (rs.eof and rs.bof) Then

       response.write "<script language='javascript'>alert('管理员名称重复,请重新填写!');history.back();</script>"

       Response.End()

   Else

       conn.Execute("insert into tb_manager(Mname,Mpasswd,Mgrade) values('"&Trim(Request.Form("txt_item")(1))&"','"&Trim(Request.Form("txt_item")(2))&"','"&Trim(Request.Form("txt_item")(3))&"')")

   End If   

   if err<>0 then  

       response.write "<script language='javascript'>alert('添加数据失败,请正确填写信息!');history.back();</script>"

       Response.End()

    else         

       Response.write "<script language='javascript'>alert('管理员信息添加成功!');window.location.href='manager_add.asp';</script>"

    end if

End If

%>

在企业网站的首页面中,用户单击左侧菜单栏中的“管理员信息管理”超链接,在打开的页面中可以浏览管理员的信息,例如:管理员名称、级别、最后登录IP、最后登录时间等。在该页面中提供了“修改”和“删除”管理员信息的入口。运行效果如图2.43所示。

图2.43  管理员信息管理页面

在管理员信息管理页面中,单击“修改”按钮可以进入到管理员信息修改页面。在管理员信息修改页面中,可以修改管理员的名称、登录密码以及管理员级别。为了保证表tb_manager与表tb_login中数据的一致性,针对表tb_manager创建一个after update触发器,在对表tb_manager进行update操作后对表tb_login也进行update操作。关键代码如下:

例程2-32  代码位置:光盘"mr"2"2.4"2.4.4"code.asp

<%

If flag="修改" Then

     id=Trim(Request.Form("id"))

     On Error Resume Next

在创建触发器前,先删除同名触发器。

     str="select name from sysobjects where name='edit_tr' and type='tr'"

      Set rs=Conn.Execute(str)

     If Not rs.eof Then Conn.Execute("drop trigger edit_tr")

     rs.close

     Set rs=Nothing

为表tb_manager创建after update触发器,根据Inserted逻辑表更新表tb_login中对应的数据。

     str="create trigger edit_tr on tb_manager after update as update tb_login set Manager=(select Mname from Inserted) where Mid in (select id from Inserted)"

     Conn.Execute(str)

在执行修改操作之前,先查看数据表中是否有重复的数据,如果存在重复数据则给出提示信息,如果不存在重复信息则执行update命令更新表tb_manager中的数据。

     Set rs=conn.Execute("select Mname from tb_manager where Mname='"&Trim(Request.Form("txt_item")(1))&"' and id<>"&id&"")

     If Not (rs.eof and rs.bof) Then

       response.write "<script language='javascript'>alert('管理员名称重复,请重新填写!');history.back();</script>"

       Response.End()

     Else

       Conn.Execute("update tb_manager set Mname='"&Trim(Request.Form("txt_item")(1))&"',Mpasswd='"&Trim(Request.Form("txt_item")(2))&"',Mgrade='"&Trim(Request.Form("txt_item")(3))&"' where id="&id&"")

     End If  

定义错误处理。

     if err<>0 then 

       response.write "<script language='javascript'>alert('修改数据失败,请正确填写信息!');history.back();</script>"

       Response.End()

      else       

       Response.write "<script language='javascript'>alert('管理员信息修改成功!');window.location.href='manager_admin.asp';</script>"

      end if

End If

%>

在管理员信息管理页面中,单击“删除”按钮可以删除对应的管理员信息。在程序处理页面中,首先针对表tb_manager创建一个after delete触发器,查询SQL Server数据库中的Deleted逻辑表删除表tb_login中对应的数据,然后再调用Connection对象的Execute方法执行delete命令删除表tb_manager中的数据。关键代码如下:

例程2-33  代码位置:光盘"mr"2"2.4"2.4.4"code.asp

<%

If flag="删除" Then

   id=Trim(Request.QueryString("recd_id"))

   On Error Resume Next

   '在创建触发器前,先删除同名触发器

   str="select name from sysobjects where name='del_tr' and type='tr'"

   Set rs=Conn.Execute(str)

   If Not rs.eof Then Conn.Execute("drop trigger del_tr")

   Set rs=Nothing

   '创建触发器,并触发触发器删除数据

    str="create trigger del_tr on tb_manager after delete as delete from tb_login where Manager in (select Mname from deleted)"

    Conn.Execute(str)

    Conn.Execute("delete from tb_manager where id="&id&"") 

    if err<>0 then 

       response.write "<script language='javascript'>alert('删除数据失败!');history.back();</script>"

       Response.End()

     else        

       Response.write "<script language='javascript'>alert('管理员信息删除成功!');window.location.href='manager_admin.asp';</script>"

     end if

End If

%>

3.补充说明

用户不仅可以在SQL Server数据库中通过创建触发器来操作数据,还可以在Oracle数据库中创建触发器。下面介绍SQL Server触发器与Oracle触发器的区别。

(1)SQL Server触发器针对的是对数据表的每一个操作而不是数据表中每一个行记录,而Oracle触发器既可以针对每一个操作,又可以针对数据表中每一行记录(即每一行记录都会触发一次触发器)。

(2)SQL Server触发器只被DML(Data Manipulation Language,数据操纵语言)语句所触发,而Oracle触发器既可以被DML命令所触发又可以被DDL(Data Definition Language,数据定义语言)语句所触发。在Oracle中还可以为系统表编写触发器。

(3)SQL Server触发器与Oracle触发器都是在事件发生时隐式运行的,并且触发器不能接收参数。

2.4.5  通过事务操作数据

使用ASP开发程序时,不仅可以通过Connection对象或者ObjectContext对象实现事务处理,还可以针对SQL Server数据库定义事务,实现对数据的一致性处理从而保证数据的完整性。

1.方案分析

在开发以SQL Server为后台数据库的网站时,可以在SQL Server数据库中定义显式事务来实现对数据的有效操作。显式事务是指可以显式地在其中定义事务的启动和结束。Transact-SQL脚本使用BEGIN TRANSACTION、COMMIT TRANSACTION或COMMIT WORK、ROLLBACK TRANSACTION 或 ROLLBACK WORK语句来定义显式事务。

l          BEGIN TRANSACTION语句

BEGIN TRANSACTION语句用于连接标记显式事务的起始点。

l          COMMIT TRANSACTION 或 COMMIT WORK语句

如果没有遇到错误,可使用该语句成功地结束事务。该事务中的所有数据修改在数据库中都将永久有效。事务占用的资源同时也将被释放。

l          ROLLBACK TRANSACTION 或 ROLLBACK WORK语句

用来清除遇到错误的事务。该事务修改的所有数据都返回到事务开始时的状态。事务占用的资源同时也将被释放。

显式事务模式持续的时间只限于该事务的持续期。当事务结束时,连接将返回到启动显式事务前所处的事务模式,即隐性模式或者是自动提交模式。

通过定义显式事务操作数据的流程图,如图2.44所示。

图2.44  通过事务操作数据的流程图

2.实施过程

*  实例位置:光盘"mr"2"2.4"2.4.5

通过事务操作数据,不仅可以避免因在操作过程中出现的异常使数据丢失等情况,还可以提高程序的执行效率。下面以明日网站的文章中心模块为例,介绍在对文章类别进行删除操作的同时删除该类别下的所有文章信息的过程中如何使用事务进行约束,运行效果如图2.45所示。

图2.45  通过事务操作数据

在文章类别管理页面中,用户可以浏览到文章类别信息列表和文章信息列表,当单击“删除”按钮时,将删除一个文章类别信息,同时可以查看到在文章信息列表中属于该类别的文章信息也被删除。具体的实现步骤如下:

(1)使用begin transaction语句标记显式事务的开始。

(2)执行delete语句先删除文章信息表tb_article中的对应类别的所有文章信息,再删除文章类别信息表tb_article_class中指定的文章类别信息。

(3)执行select @@error语句并返回一个记录集,如果该记录集返回的值不为0,则说明执行过程中出现异常,这时使用rollback transaction语句撤消事务,使数据库中的数据恢复到执行操作之前的状态;如果返回值为0,则使用commit transaction语句提交事务,成功完成本次操作。

代码如下:

例程2-34  代码位置:光盘"mr"2"2.4"2.4.5"index.asp

<%

   If Trim(Request.Form("flag"))="删除" Then

     id=Trim(Request.Form("id"))

     conn.Execute("begin transaction f4")

     conn.Execute("delete from tb_article where Aclass="&id&"")

     conn.Execute("delete from tb_article_class where id="&id&"")

     Set rs=conn.Execute("select @@error")

     If rs(0)<>0 Then

       conn.Execute("rollback transaction f4")

       Response.Write("<script language='javascript'>alert('出现异常错误,事务回滚!');window.location.href='index.asp';</script>")

       Response.End()

     Else

       conn.Execute("commit transaction f4")

       Response.Write("<script language='javascript'>alert('事务已提交!');window.location.href='index.asp';</script>")

     End If

     rs.close

     Set rs=Nothing

   End IF

%>

3.补充说明

在SQL Server数据库和Oracle数据库中都可以实现对事务的处理,下面介绍两种数据库对事务处理的区别。

(1)两种数据库是以不同的方式处理事务隔离级别的。SQL Server数据库采用锁来处理4个隔离级别(级别1 Read Uncommitted;级别2 Read Committed;级别3 Repeatable Read;级别4 Serializable),从而提高事务的隔离性。Oracle数据库通过读日志来避免脏读(dirty read)和不可重复读(non-repeatable read)这两个事务缺陷,读日志的方式能够保证其他事务能够读到修改之前的数据。

(2)SQL Server数据库采用的是显式事务,当发出SQL命令后系统会立即执行相应的操作,而无须使用commit命令。Oracle数据库采用的是隐式事务。

posted @ 2009-04-01 05:05  jiania  阅读(1064)  评论(0)    收藏  举报