局域网图书资料查询系统
作者:未知 时间:2006-09-06
本文介绍了一种基于PowerBuilder6.5的局域网图书资料查询系统的设计与实现。该系统采用C/S架构,利用PowerBuilder6.5作为开发工具,SQL Anywhere5.0 Server作为数据库服务器,SQL Anywhere Cient作为客户端。系统主要功能包括用户管理、图书分类查询、多条件查询、模糊查询、更新与编辑、打印输出等。本文详细阐述了数据库理论基础、PowerBuilder6.5及其数据库编程、系统需求分析、功能模块划分、数据库设计以及应用程序设计等内容,实现了图书资料的查询、更新、打印等功能,方便了科研或开发小组对图书资料的管理和利用。
第一章 绪 论
§1.1 数据库技术
数据库技术作为数据管理技术,是计算机软件领域的一个重要分支,产生于60年代末。现已
形成相当规模的理论体系和实用技术。优秀的数据库设计是应用成功的基石。万万丈高楼平地起
,数据库设计如同高楼的基石,是开发高品质应用的前提。
1.1.1 数据库的体系结构
数据的体系结构分成三级:内部级(Interna),概念级(Conceptua)和外部级(Externa)。这
个三级结构有时也称为“三级模式结构”。
外部级:最接近用户,是单个用户所能看到的数据特性。单个用户使用的数据视图的描述称为
“外模式”。
概念级:涉及到所有用户的数据定义、是全局的数据视图。全局视图的描述称为“概念模式”
。
内部级:最接近于物理存储设备,涉及到实际数据存储的结构物理存储数据视图的描述称为“
内模式”。
数据库的三级模式结构是数据的三个抽象级别。它把数据的具体组织留给DBMS
去做,用户只要抽象地处理数据,而不必关心数据在计算机中的表示和存储,这样就减轻了用户
使用系统的负担。
1.1.2 数据库管理系统(DBMS)
数据库管理系统(DBMS)是指数据库系统中管理数据的软件系统。DBMS是数据库系统的核心组成
部分。对数据库的一切操作,包括定义、查询、更新及各种控制,都是通过DBMS进行的。
在不同的计算机系统中,由于缺乏统一的标准,即使同种数据模型的DBMS,它们在用户接口、系
统功能方面也常常是不相同的。
用户对数据库进行操作,是由DBMS把操作从应用程序带到外部级、概念级、再导向内部级,进而
操作存储器中的数据。DBMS的主要目标,是使数据作为一种可管理的资源处理。
DBMS的主要功能为:
数据库定义功能:DBMS提供数据定义语言(DDL)定义数据库的三级结构,包括外模式、概念模
式、内模式及基相互之间的映象,定义数据的完整性、安全控制等约束。因此,在DBMS中应包括
DDL的编译程序。
数据库的操纵功能:DBMS提供数据操纵语言(DML)实现对数据库中数据的操作。基本的数据操
作分成两类四种:
² 检索(查询)
² 更新(插入、删除、修改)
数据库的保护功能:数据库中的数据是信息社会的战略资源,对数据的保护是至关重要的大事
。DBMS对数据库的保护主要通过四个方面实现:
² 数据库的恢复:在数据库被破坏或数据不正确时,系统有能力把数据库恢复到正确的状态。
² 数据库的并发控制:DBMS的并发控制子系统能防止错误发生,正确处理好多用户、多任务环境
下的并发操作。
² 数据库的完整性控制:保证数据库中数据及语义的正确性和有效性,防止任何对数据造成错误
的操作。
² 数据库的安全性控制:防止未经授权的用户蓄谋或无意地存取数据库中的数据,以免数据的泄
露、更改或破坏。
² 数据库的存储管理:把各种DML语句转换成低层的文件系统命令,起到数据的存储、检索和更新
的作用。
² 数据库的维护功能:它有许多实用程序提供给数据库管理员:
Ø 数据装载程序
Ø 备份程序
Ø 文件重组织程序
Ø 性能监控程序
² 数据字典:数据库系统中存放三级结构定义的数据库称为数据字典(DD)。对数据库的操作都
要通过访问DD才能实现,通常DD中还存放数据库运行时的统计信息。
1.1.3 PowerBuider与数据库
n PowerBuider与数据库是“配合”与“协调”的关系
PowerBuider是客户/服务器体系结构下客户端的开发工具,用于开发客户应用程序。这个程序首
先建立一个与数据库的通信渠道,然后将用户的需求以某种方式传送给数据库服务器。在应用程
序接收到数据库服务器返回的数据后,它分析返回的数据并呈现给用户。因此我们说,客户应用
程序只完成请求和表现数据的工作,是用户操作计算机的人机界面,大多数数据处理是由服务器
完成的。
数据库数据器是一个存取数据和管理数据的软件,它针对客户的请求为客户提供数据服务。这些
服务包括数据插入、修改和查询等。客户对数据库服务器提出请求用的语言是SQL(Strucrured
Query Language)。SQL是大多数数据库服务器使用的查询语言。
因此我们说,PowerBuider与数据库的关系是“配合”与“协调”的关系。PowerBuider完成数
据请求、数据表现、菜单、界面等表象方面的工作,而数据库服务器完成数据库的存储管理、并
发控制、事务管理、完整性维护、查询优化等工作。
§1.2 局域网图书资料查询系统
1.2.1 局域网(LAN)
计算机网络是指将多台具有独立功能的计算机,通过通信线路和通信设备连接起来,在网络软件
的支持下实现数据通信和资源共享的计算机系统。
计算机网络的规模有大有小,大的可以覆盖全球,小的仅局限于一个办公室。现在一般按照网络
覆盖的地理范围将计算机网络分为三类:局域网(LAN)、城域网(MAN)、广域网(WAN)。
局域网是覆盖范围在10公里以内的计算机网络。局域网传输速度快,一般局限在一个单位内部,
例如一所学校或一家企业。
1.2.2 应用程序开发背景
一个数十人的科研或开发小组,搜集了上千册有用的图书资料,其中有一部分分布在个人手中,
为了方便大家查阅,需要对每本书的状态进行跟踪,另新进和丢失的图书资料必须得到及时的反
映。
1.2.3 系统功能
图书分类查询,多条件查询,模糊查询
用户必须登录方能执行各种操作,允许用户在客户机或浏览器修改,添加
删除图书资料,实行松散管理,这建立在用户高度自觉的基础之上,当然也可由管理员在服务器
上直接对数据库操作。
能将查询结果生成报表,并打印输出。
1.2.4 系统运行环境
该系统采用Cient/Server模式进行设计:局域网中有一台服务器,其上运行服务器程序,操作系
统为windows2000 server,客户机操作为Windows98,其上运行客户端程序。
1.2.5 系统开发工具
该系统采用PowerBuider6.5进行开发,数据库服务器端为SQL Anywhere5.0 Server;客户端则
为SQL Anywhere cient,整个系统在SQL Anywhere Loca端调试完成。
§1.3 本文所作工作
首先,在绪论部分介绍了局域网图书资料查询系统的应用背景、开发环境以及选用的开发工具与
数据库的关系,阐明了局域网的概念。并对数据库的体系结构、DBMS进行了介绍。
第二章的开始介绍了关系型数据库的基本概念,着重说明了几个关键概念的定义;然后对SQL语言
作了一个介绍说明;最后通过两个例子介绍了PB6.5用PowerScript语言调用SQL的方式。
第三章从特点和功能入手,介绍了开发工具PowerBuider6.5;并且介绍了C/S模式的概念、特点
以及C/S模式与开发工具PowerBuider6.5的联系;在这个章节的最后简单介绍了PB6.5对数据库的
操作。
第四章用软件工程的方法分析了局域网图书资料查询系统,对整个系统进行了需求分析、功能模
块划分,并通过ER图对数据库进行概念设计、用Microsoft Access对数据库进行逻辑设计。
第五章是对局域网图书资料查询系统的具体设计。描述了整个系统详细的功能模块划分,描述了
登录模块、模糊(分类)查询、多条件(组合)查询、数据编辑更新模块以及数据维护模块的实
现过程,并对设计源代码进行了注释分析。
最后,在结束语的总结部分指出了系统的亮点以及不足之处。简单介绍了自己开发过程中的体会
与心得:在摸索中实践,在实践中摸索。
第二章 数据库理论基础
§2.1 关系型数据库
2.1.1 关系模型的基本概念
用二维表格结构表示实体集,外键表示实体间联系的数据模型称为关系模型。
1. 二维表格
表2-1是一张职工登记表,这是二维表格
工号 姓名 年龄 性别 工资
0001 Zhang 26 男 1000
0002 Li 25 女 1500
0003 Liu 29 男 1000
0004 Wang 22 女 1500
表2-1 二维表格实例
为简单起见,对表格数学化,用字母表示表格的内容。表2-1可用图2-1表示:
A B C D E
A1 A2A3 A4 B1B2B3B4 C1C2C3C4 D1D2D3D4 E1 E2E3E4
2. 键(KEY)
键由一个或几个属性组成,在实际使用中,有下列几种键:
1) 超键(Super Key):在关系中能惟一标识元组的属性集称为关系模式的超键。
2) 候选键(Candidate Key):不含有多余属性的超键称为候选键。也就是在候选键中,若要再删
除属性,就不是键了。
3) 主键:(Primary Key):用户选作元组标识的一个侯选键称为主键。一般,如不加说明,则键
是指主键。
3. 关系的定义和性质
我们可以用集合的观点定义关系。关系是一个元数为K(K>=1)的元组的集合。
把关系看成是一个集合,集合中的元素是元组,每个元组的属性个数应相同。在关系模型中,对
关系作了下列规范性限制:
1) 关系中每一个属性值都是不可分解的。
2) 关系中允许出现相同的元组(没有重复元组)
3) 由于关系是一个集合,因此不考虑元组间的顺序,即没有行序。
4) 元组中,属性在理论上也是无序的,但在使用时按习惯考虑列的顺序。
2.1.2数据库的设计理论
关系数据库的设计理论主要包括三个方面的内容:数据依赖、范式,模式设计方法。其中数据依
赖起着核心的作用。
1. 函数依赖(Functiona dependency , FD)的定义
设R(U)是一个关系模式,U是R的属性集合,X和Y是U的子集。对于R(U)的任何一个可能的关系r,
如果r中不存在两个元组,它们在X上的属性值相同,而在Y上的属性值不同,则称“Y函数依赖于X
” ,记作X→Y。如果X→Y,并且对于X的任一真子集X ’,都有Y 不函数依赖于X ’,则称“Y完
全函数依赖于X” ,记作X f Y 。若X→Y,但Y不完全函数依赖于X,则称“Y部分函数依赖于X”
,记作X P Y 。如果X→Y,Y→Z,且Y≮ X, X不函数依赖于Y,则称“Z传递函数依赖于X”。
2. 范式
在对表的形式进行了规范化定义后,数据结构还有五种规范化定义,定名为规范化模式,称为范
式。在这五种范式中,一般只用前三种,对于常用系统就足够了。而且这五种范式是“向上兼容
”的,即满足第五范式的数据结构自动满足一、二、三、四范式,满足第四范式的数据结构自动
满足第一、二、三范式,……,依此类推。
第一范式(first norma form,简称1st NF)就是指在同一表中没有重复项出现,如果有则应将重
复项去掉。这个去掉重复项的过程就称之为规范化处理。在本文所讨论的开发方法里,1st NF实
际上是没有什么意义的。因为我们按规范化建立的指标体系和表的过程都自动保证了所有表都满
足1st NF。
第二范式(second norma form,简称 2nd NF)是指每个表必须有一个(而且仅一个)数据元素为主
定义为主关键字(其它数据元素中的记录数据都有可能重名,故不能作为主关键字),故只要知道
了一个合同记录的合同号,就可以唯一地在同一行中找到该合同的任何一项具体信息。通常我们
称这种关系为函数依赖(functiona depEndence)关系。即表中其它数据元素都依赖于主关键字,
或称该数据元素唯一地被主关键字所标识。
第三范式(third norma form,简称 3rd NF)就是指表中的所有数据元素不但要能够唯一地被主
了 2nd NF的数据结构来说,表中有可能存在某些数据元素依赖于其它非关键宇数据元素的现象,
必须加以消除。
为防止数据库出现更新异常、插入异常、删除异常、数据冗余太大等现象,关系型数据库要尽量
按关系规范化要求进行数据库设计。下面以教务管理信息系统为例来进行分析。
3. 模式设计方法
一个好的模式设计方法应符合下列三条原则:
表达性:涉及到两个数据库模式的等价性问题,即数据等价和依赖等价,分别用无损联接和保
持函数依赖来衡量。
分离性:是指属性间的“独立关系”应该用不同的关系模式表达。独立联系是我们所考虑的“
基本信息单位”。实际上分离就是清除存储异常和数据冗余现象。如果能达到这个目的,就分离
。分离的基准就是一系列范式,分离与依赖等价有时是不可兼容的。
最小冗余性:要求在分解后的数据库能表达原来数据库的所有信息这个前提下实现。目的就是
节省存储空间,提高对关系的操作效率,清除不必要的冗余。但要注意,在实际使用中,并不一
定要达到最小宙余。因为有时带点冗余对于查询处理是有好处的。
关系模式的方法基本上可以分为分解与合成两大类。分解型算法要求输入一个
初始模式集和依赖集,而结果满足数据等价要求。对于合成型算法只要求输入初始依赖集,结果
满足依赖等要求。但它们依据的基本思想是共同的,即独立的联系独立表示。
§2.2 SQL语言介绍
SQL(Structured Query Language)即“结构式查询语言”。SQL虽然名为查询语
言,但实际上具有定义、查询、更新和控制等多种功能。由于它使用方便、功能丰富、语言简单
易学,很快得到应用和推广。从20世纪70年代末起,在推出的关系数据库系统产品ORACLE、SQL/
DS、DB2、SYBASE上实现了SQL语言。很快,SQL语言被整个计算机界认可。1987年6月,国际标准
化组织(ISO)采纳为国际标准。随后,ISO对标准进行了大量的修改和扩充。在1992年推出了新
的标准-SQL2。SQL的标准化工作还在继续,新的标准已被命名为SQL3,将包括许多新的数据库概
念,正在不征求意见和进行修改
这里将简单介绍基于SQL89和SQL2的语言使用概貌:
2.2.1 SQL的组成
SQL主要分成四个部分:
1)数据定义:这一部分也称为“SQL DDL”,用于定义SQL模式、基本表、视图和索引。
2)数据操纵:这一部分也称为“SQL DML”。它分为数据查询和数据更新两类。其中数据更新又
分成插入、删除、和修改三种操作。
3)数据控制:这一部分包括对基本表和视图的授权,完整性规则的描述,事务控制等内容。
4)嵌入式SQL的使用规定:这一部分内容涉及到SQL语句嵌入在宿主语言程序中使用的规则。
2.2.2 SQL的数据查询
n SELECT语句的语法
SELECT 目标表的列名或列表达式序列
FROM 基本表和(或)视图序列
[WHERE行条件表达式]
[GROUP BY列名序列
[HAVING组条件表达式]]
[ORDER BY列名[ASC|DESC]…]
句法中表示该成分可有,也可无。
整个语句的执行过程如下:
a) 读取FROM子句中基本表、视图的数据,执行笛卡尔积操作。
b) 读取满足WHERE子句中给出的条件表达式的元组。
c) 按GROUP子句中指定列的值分组,同时提取满足HAVING子句中组条件表达式的那些组。
d) 按SELECT子句中给出的列名或列表达式求值输出。
e) ORDER子句对输出的目标表进行排序,按附加说明ASC升序排列,或按DESC降序排列。
SELECT语句中,WHERE子句称为“行条件子句”,GROUP子句称为“分组
子句”,HAVING子句称为“组条件子句”,ORDER子句称为“排序子句”。
2.2.3 SQL的数据更新
SQL的数据更新包括数据插入、删除和修改等三种操作
1)数据插入
a) 元组值的插入
INSERT INTO 基本表名(列名表)
VALUES(元组值)
或者 INSERT INTO 基本表名(列名表)
(TABLE(元组值),
(元组值),
……)
前一种格式只能插入一个元组,后一种格式可以插入多个元组。
2)数据删除
SQL的删除操作是指从基本表删除元组,其语法如下:
DELETE FROM 基本表名
[WHERE条件表达式]
其语义是从基本表中删除满足条件表达式的元组。
3)数据修改
当需要修改基本表中元组的某些列值时,可以用UPDATE语句实现,其句法如下:
UPDATE 基本表名
SET 列名=值表达式[,列名=值表达式…]
[WHERE条件表达式]
其语义是:修改基本表中满足条件表达式的那些元组中的列值,需修改的列值在SET子句中指出。
§2.3 PB6.5对数据库的操作
PowerBuider对数据库的操作即可以通过数据窗口完成(本质上是数据窗口把在屏幕上对数据
库的操作转化成SQL语句),又可以在PowerScript语言中直接调用SQL或存储过程(本质上仍是SQL
语句)完成。下面通过列举两个简单实例来描述在PowerScript程序调用SQL的方式:
1、SELECT语句查询返回一行
如果SQL SELECT语句返回的结果只有一行,可以在PowerScript中书写以下的SQL语句:
SELECT 列名1,列名2,列名3,…
INTO:变量1,:变量2,:变量3…
FROM 表名1,表名2,表名3,…
WHERE…
其中,“变量1”,“变量2”和“变量3”等都是PowerScript语言的变量。该语句的作用是把数
据库表的“列名1”,“列名2”和“列名3”等列的值从数据库中取出,然后放入对应的“变量1
”,“变量2”和“变量3”等变量中。例如:
String name, extrace
SELECT name,extract INTO : name , : extrace from auths
Where author_code=’A00001’;
该语句仅仅适合于查询到一条记录的情况。如果查询到的记录是多条,则要用下面的方法。
2、查询多行
SELECT是描述型语言,它面向的是集合,是一组记录。而PowerScript语言却是面向过程的,它要
一条条地接收并处理记录。PowerScript通过描述型光标(CURSOR)在这组记录上游动的方法,给
Script语句逐个地传送记录,建立了集合与记录间的内在联系。请看下例:
String name
String V1=’A%’
DECLARE CURSOR FOR fie://定义光标,光标名为C1
SELECT name FROM auths fie://光标对应的SELECT语句
WHERE author_code ike :V1; fie://SELECT语句中用到了变量V1
OPEN C1; // 打开光标,此时执行此光标对应的SQL SELECT语句。
Lab1:
FETCH C1 INTO :name; fie://取记录,光标下移一条
If SQLCA.SQLCode=0 then fie://取记录成功
Goto ab1 fie://如果成功取出记录,则取下一条
End if
打开光标的时候,变量V1被其值“A%”替换,因此最后执行的SQL SELECT语句为:
SELECT name FROM auths
WHERE author_code ike ‘A%’
执行完这条语句后,把光标C1定位到了查询出的第一条记录上。每执行一次语句:
FECTCH C1 INTO :name ;
则取出一条记录送给变量name,然后光标移到下一条记录。如果想控制查询结果的次序,必须在
SELECT语句中用ORDER BY子句,否则,没有其它的办法。我们不可能一下子把光标定位在某条记
录上,只能从第一条开始,一个个地移。
第三章 PB6.5及其数据库编程
§3.1 开发工具PowerBuider6.5
3.1.1 PowerBuider6.5特点及功能
要适应企业环境不断变化的需求,成功地开发出高质量的应用系统,必须采用先进的应用开发工
具。这对于减轻应用开发人员的开发负担,提高开发速度和质量都有十分重要的意义。Sybase公
司推出的PowerBUider6.0/6.5是用于Cient/Server、Web及组件开发的企业级应用开发工具。它
占全球开发工具市场近50%,是当前最优秀的开发工具之一。它具有以下优异的功能和特点:
1、内置的关系数据库
PowerBuider本身带有一套数据库系统Sybase SQL Anywhere。这样做的好处是, PowerBuider
可以脱离网络数据库服务器独立运行,从而在开发阶段脱离网络服务器上的数据库。当然,利用
这一功能也可以开发和调试单用户的独立的数据库应用。下图显示了内置数据库的工作原理:
如果没有内置的数据库Sybase SQL Anywhere,开发时则不能脱离网络服务器上的数据库(如图3
-2所示):
2、数据窗口(Datawindow)对象
PowerBuider拥有数据窗口这个具有专利技术的智能对象,利用该对象可以操作关系数据库的数
据而无需编写SQL语句。用户可以查询、修改、插入、删除、浏览、打印、以多种文件格式打开和
存储数据,或在数据窗口中直接定义功能按钮实现预定义的系统功能,如插入、删除数据的操作
。它还支持数据库事务管理和并发控制等机制。其工作机理如下图所示:
3、丰富的数据窗口数据源和多种样式的数据显示格式
数据窗口可以用来维护数据和显示数据,可定义多种显示风格和数据显示格式;并且还可以与
TreeView控制、ListView控制配合使用,创建出更丰富的数据显示格式
4、支持多种商业图形,包括多种类型的二维和三维的图形
5、支持组件的开发和调用
用PowerBuider开发的组件,可以在其它应用中调用,可以由多种事务管理服务器管理。
6、具有面向对象的特征
PowerBuider采用了面向对象的开方式,这可以使系统开发人员在无需精通专用语言的情况下就
可以迅速转向面向对象的开发。PowerBuider应用是由一系列对象组成的,包括窗口、菜单、函
数、数据窗口和各种控制等对象,它支持对象的继承、封装和多态性。
7、有机结合的集成开发环境
8、完全支持Windows的窗口信息和控制
9、强有力的PowerScript编程语言
它能使开发人员很容易地将简单或复杂的事务逻辑与应用相配合。该语言还有几百个函数用于操
纵对象、处理数字、文本、字符串、日期和应用分布,进行文件处理、报表打印,用DDE和OLE 2
.0进行程序之间的通信,直接调用SQL语句操纵数据库等等。
10、PowerBuider提供了多种流行软件的接口库
Netware Library
Pen Computing Library
Lotus Notes Library
Microsoft MAPI
11、PowerBuider支持多种平台
目前,PowerBuider能够在Microsoft Windows 3.X、Windows 95Windows NTApha/InteAppe
Mac Sun Saoris IBM AIX HP Unix等多种平台上开发和运行应用程序,并能够不加改动地应用于
其它平台上。
12、支持Internet/Intranet下的Web应用开发
13、支持团体开发
14、对多种数据库的支持
PowerBuider几乎支持所有的数据库,它提供了到多种数据库的专用接口和ODBC接口。
§3.2 PowerBuider与Cient/Server体系结构
3.2.1 Cient/Server模式
在C/S结构中,存在着几个非常重要的基本概念,它们是:主机、终端、客户机、工作站和服务器
。在分析C/S网络结构之前,必须搞清楚它们之间的区别。
最早的计算机网络是伴随着主机(Host)和终端(Termina)这两个概念的出现而产生的。当时的
主机通常是指具有中央处理单元(CPU)的大型机或功能较强的小型机,而终端则是指计算机的输
入输出设备。终端没有自己的CPU,当然也没有自己的内存,其主要功能是将键盘输入的请求数据
发往主机并将主机的运算结果显示出来。主机和终端共同构成了集中式系统结构。在这种应用系
统中,几乎所有的工作都是由主机来完成,终端仅仅作为一种输入输出设备,因此系统负荷重、
效率低、扩充性差。
之后随着计算机网络结构的细化,不同的计算机开始在网络中担负不同的任务,于是出现了文件
服务器/网络工作站(F/W)式结构的模型。其中,工作站(Workstation)和服务器(Server)都
是独立的计算机。当一台连入网络的计算机向其它计算机(工作站)提供各种网络服务(如数据
、文件的共享)时,它就被叫做服务器。而那些用于访问服务器资料的计算机则被叫做工作站。
在F/W结构中,所有实际的数据处理工作仍在运行数据库应用程序的PC工作站上完成,因此不论文
件服务器的性能有多高,其整体网络性能都将受到PC机能力的限制。
客户机(Cient)是伴随C/S数据访问的兴起而被提出来的,在一般人的理解中它和F/W概念没有
本质的区别。但是,严格说来,C/S模型并不是从物理分布的角度来定义的。它既包括具体的网络
结构设计,又包括软件的运行和组织,所体现的是一种网络数据的访问方式。这里的客户机和服
务器不仅指承担不同任务的计算机本身,而且包括主机上运行的客户端和服务器端的软件环境。
它们的区别,是相对于各自在网络数据库访问中所处的地位或实现的功能而言的。
理解了以上的基本概念,才可以更好地理解C/S结构的实质和运行方式:所谓C/S结构,是将数据
存取与应用程序分离开来,把一个软件系统或应用系统按功能分成若干个部分,再将这些软件的
组成部分按其不同的角色分成Cient软件和Server软件,分别放置在客户机和服务器上。客户机
程序负责用户交互界面、数据表示及应用处理逻辑等应用部分,而服务器端则负责数据存取管理
、完整性控制及并发控制等数据库管理部分。客户机程序应用通过SQL语句访问数据库,相应的
SQL语句经网络传输到服务器端,由服务器端的数据库服务器解释执行这些SQL语句,执行后的结
果数据送回客户机。
3.2.2 Cient/Server体系结构的优缺点
我们看到,客户/服务器体系结构有以下优点:
应用逻辑与数据实现分离,实现了在网络上的负载均衡;
充分利用了网络服务器的处理能力。客户中需将请求送数据库服务器,数据查询工作由服务器
来承担,服务器的能力可以得到充分的发挥。
但是它也有以下一些缺点:
由于计算机技术的快速进步和企业需求变化的加快,企业面临的硬件、网络操作系统、数据库
系统、开发工具、应用系统的升级周期越来越短,因此“维护”客户服务器的费用就变得越来越
高昂。
快速升级的硬件、网络操作系统、数据库系统、开发工具使企业的技术人员失去了方向:不断
地消化新技术,却无瑕顾及企业要解决的问题
在客户端,必须安装操作系统(一般为Windows95/NT)、网络软件、特定的中间件(Sybasse
Net-Library)以及应用软件系统才能工作,因此应用系统的安装、升级和维护通常需要专业人员
才能用胜任,且必须各站点逐个安装,从而使客户端的维护费用变得也很高昂。对主机系统来说
,这一点要优越的多,只需要在主机上安装一次即可。
3.2.3 PowerBuider是客户/服务器体系结构下客户端的开发工具
前面已提到,PowerBuider是客户/服务器体系结构下开发客户程序用的开发工具,用
PowerBuider开发出的程序可以存取数据库中的数据。下图表示了PowerBuider与数据库配合工
作的方式。
我们看到,PowerBuider作为客户端的应用开发工具,主要完成的是表示逻辑方面的工作,例如
,菜单、录入界面。而数据库服务器管理的是事务逻辑和数据存取方面的工作。因此,
PowerBuider开发工作应分为两部分,一部分是前台表示逻辑方面的工作,另一部分是数据库后
台方面的设计工作(如数据库触发器、存储过程和视图等的设计)。一个好的应用系统,前台开
发与后设计应是有机结合、合理分布的;良好的后台设计可以降低前台的开工作量,提高系统的
运行效率。
§3.3 PowerBuider6.5数据库编程
PowerBuider与数据库的关系是“配合”与“协调”的关系。PowerBuider完成数据请求、数据
表现、菜单、界面等表象方面的工作,而数据库服务器完成数据库数据的存储管理、安全管理、
并发控制、事务管理、完整性维护、查询优化等工作。
PowerBuider在操作数据库时与以下几方面有关:
1、在数据库画笔中定义数据库表和视图
定义表的结构
表中列的扩展属性
² 定义表中列的显示风格
² 定义表中列的编辑屏蔽
² 定义表中列的校验
定义表的主键
定义表的外部键
定义表的索引
PowerBuider有五个系统表,这五个系统表是PowerBuider初次连接到数据库时系统自动建立的
。下表给出了这个系统表的表名和它们的作用
PowerBuider系统表 用途
PBCATTBL 存放表或视图,表或视图中列的缺省字体。
PBCATCOL 存放列用到的显示风格名、校验规则名和编辑风格名;列的标题、列的标签、字体的大
小写,字体的对齐方式。
PBCATFMT 列的显示风格定义。
PBCATVLD 列的校验规则定义。
PBCATEDT 列的编辑风格定义。
表3-1 PowerBuider系统表
2、在数据库画笔中在图形方式下操作数据库
这些操作包括:
插入记录
修改记录
删除记录
查询记录
把查询出的记录存入文件
把表或视图的定义转变成建表的SQL语名
这些功能是给开发人员和管理人员提供的。
3、在数据库画笔中用SQL语句执行平台管理操作数据库
生成数据库
管理数据库
维护数据
在这里创建的表和规则不会记录在PowerBuider系统表中
4、在查询画笔中定义查询对象
如果一个查询动作要多次使用,还可以用PowerBuider的查询画表生成查询对象这个查询对象不
能由数据库画表调用,它只能由查询画表本身调用执行。在建立数据窗口时,数据源也可以做在
查询对象之上。
事实上,查询对象就是写好了的SQL语句,它存在PowerBuider的pb文件中,在使用的时候调用
即可。
5、用数据管道在不同数据库之间转换数据
数据管道允许把一个数据库的数据(一个或多个表中的全部或部分行)转入到
另一个数据库的一个表中(这个表可以存在或不存在),从而可以使数据在不同数据库之间相互
复制。
6、用数据窗口操作数据库
Datawindow是PowerBuider操作数据库的重要的手段,通过数据窗口可以查
修改、插入和删除数据库的数据。PowerBuider数据窗口操作数据库的能力非常强,是
PowerBuider的精华所在。
数据窗口的数据源可以是:
² 表或视图
² 多表连接
² 查询对象
² 外部数据源
² 存储过程
数据窗口列数据的显示风格
数据窗口列的编辑屏蔽
数据窗口列的校验
7、PowerScript直接调用SQL语句操作数据库
PowerBuider对数据库的操作即可以通过数据窗口完成(本质上是数据窗口屏幕上对数据库的操作
转化成SQL语句),又可以在PowerScript语言中直接调用SQL或存储过程(本质上仍是SQL语句)完成
。详细操作在第*个章节已经介绍。
第四章 局域网图书资料查询系统设计分析
根据数据库系统生存期的设计方法,从数据库应用系统和开发的全过程来考虑,将数据库应用系
统设计分为以下几个阶段(见图4-1)
1)规划;
2)需求分析;
3)概念设计;
4)逻辑设计
5)物理设计
§4.1 应用需求分析
要设计一个良好的局域网图书资料查询系统,就必须首先明确该应用环境对系统的要求。局域网
图书资料查询系统的应用背景为:一个数十人的科研或开发小组,搜集了上千册有用的图书资料
,其中有一部分分布在个人手中,为了方便大家查阅,需要对每本书的状态进行跟踪,另新进和
丢失的图书资料必须得到及时的反映。因此,该系统需满足以下几方面需求:
用户的管理:必须具有使用权限的用户才能成功登录到系统中来。所谓用户权限在这里并不要
求有功能上具体的划分,集中实行松散管理,这建立在用户高度自觉的基础上。所以,在这里只
需给每个需要使用系统的人一个用户名和密码,即可登录系统进行各种操作。新的用户需要取得
管理员的许可将其加入系统,加入系统的用户可以对自己的用户密码进行修改。
查询功能:系统需要提供几种不同方式的查询手段,以实现灵活方便地管理整个系统。
² 图书分类查询:一本图书包括书名、出版社、作者、保管人等多个信息,这就要求系统能按照
不同的信息类别对图书进行查询。比如说,按书名查询、按作者查询等。选定需要的查询信息类
别,再输入想查询的内容即可查询到相关的图书信息。
² 多条件查询:很多时候,用户需要了解的信息不仅仅局限于一个条件,比方说想同时查询书名
为“数据库原理”但出版社仅为“经济科学出版社”的图书信息时,单纯的分类查询就不能满足
用户的需要,这时就要用到多条件查询。也就是说,多条件查询不仅可以实现单个的分类查询,
还可以实现多条分类查询的组合查询。每个查询条件之间用“并且”或“或者”的关系关联起来
组成完整的查询条件。
² 模糊查询:对于用户来说并不一定完全记得某本图书准确的名称,类似,对于作者、出版社等
等信息来说,很多时候用户只是记得一些相关的信息而不是一字不差的准确信息。这个时候就要
用到模糊查询。用户输入查询内容后,系统将会把包含查询内容的(注意:并不是精确的等于查询
内容)所有相关图书信息显示出来,以使用户得到准确的、自己真正需要的信息内容。
更新与编辑:
² 更新:系统允许用户对查询到的内容进行修改并且存盘。
² 编辑:系统允许用户对现库进行插入、删除的操作,保证现库的真实性与实时性。
打印输出:系统可以将用户查询到的内容动态地生成报表,并打印输出。
§4.2 系统功能模块划分
局域网图书图书资料查询系统功能划分模块图如下:
§4.3 系统数据库设计
4.3.1 概念设计
概念设计的目标是产生反映局域网图书资料查询系统需求的数据库概念结构,即概念模式。概念
模式是独立于数据库逻辑结构,独立于支持数据库的DBMS,不依赖于计算机系统的。
ER模型
ER模型是对现实世界的一种抽象。它的主要成分是实体、联系和属性。使
用这三种成分,我们可以建立许多应用环境的ER模型。
ER模型的操作
在利用ER模型进行数据库概念设计的过程中,常常需要对ER图进行种种
变换。这些变换又称为ER模型的操作,包括实体类型、联系类型和属性的分裂、合并和增删等等
。
利用ER方法的数据库概念设计
利用ER方法进行数据库的概念设计,可以分成三步进行:首先设计局部ER
模式,然后把各局部ER模式综合成一个全局ER模式,最后对全局ER模式进行优化,得到最终
的ER模式,即概念模式。
1. 设计局部的ER模式
通常,一个数据库系统都是为多个不同用户服务的。各个用户对数据的观点可能不一样,信息处
理需求也可能不同。在设计数据库概念结构时,为了更好地模拟现实世界,一个有效的策略是“
分而治之”,即先分别考虑各个用户的信息需求,形成局部概念结构,然后再综合成全局结构。
在ER方法中,局部概念结构又称为局部ER模式,其图形表示称为ER图。
实体和属性的定义如下:
图书(资料编号,资料名称,作者,出版社,出版日期,ISBN,资料类别,购买日期,保管人,
备注)
用户(编号,用户代码,用户姓名,登录口令,使用权限,查询显示项,用户显示头像)
资料类别(资料编号,资料类别)
出版社(出版社编号,出版社)
2. 联系定义:
ER模型的“联系”用于刻画实体之间的关联。一种完整的方式是对局部结构中任意两个实体类型
,依据需求分析的结果,考察局部结构中任意两个实体类型之间是否存在联系。若有联系,进一
步确定是1:N,M:N,还是1:1等。还要考察一个实体类型内部是否存在联系,两个实体类型之间是
否存在联系,多个实体类型之间是否存在联系,等等。联系定义如图4-5所示。解释如下:
一个用户可以保管多本图书资料,而一本图书资料只能由一个用户保管;
一个资料类别可以对应多本图书,而一本图书只对应一个资料类别;
一本图书由一个出版社出版,而一个出版社可以出版多种图书。
3. 设计全局ER模式
所有局部ER模式都设计好了后,接下来就是把它们综合成单一的全局概念结构。全局概念结构不
仅要支持所有局部ER模式,而且必须合理地表示一个完整、一致的数据库概念结构。
1)确定公共实体类型
为了给多个局部ER模式的合并提供开始合并的基础,首先要确定各局部结构中的公共实体类型。
在这一步中我们仅根据实体类型名和键枕认定公共实体类型。一般把同名实体类型作为公共实体
类型的一类候选,把具有相同键的实体类型作为公共实体类型的另一类候选。
2)局部ER模式的合并
合并的原则是:首先进行两两合并;先和合并那些现实世界中有联系的局部结构;合并从公共实
体类型开始,最后再加入独立的局部结构。
3)消除冲突
冲突分为三类:属性冲突、结构冲突、命名冲突。
设计全局ER模式的目的不在于把若干局部ER模式形式上合并为一个ER模式,而在于消除冲突,使
之成为能够被所有用户共同理解和接受的同一的概念模型。
3)全局ER模式的优化
在得到全局ER模式后,为了提高数据库系统的效率,还应进一步依据处理需求对ER模式进行优化
。一个好的全局ER模式,除能准确、全面地反映用户功能需求外,还应满足下列条件:实体类型
的个数要尽可能的少;实体类型所含属性个数尽可能少;实体类型间联系无冗余。
4.3.2 逻辑设计
由于概念设计的结果是ER图,DBMS一般采用关系型,因此数据库的逻辑设计过程就是把ER图转化
为关系模式的过程。由于关系模型古有的优点,逻辑设计可以充分运用关系数据库规范化理论,
使设计过程形式化地进行。设计结果是一组关系模式的定义。
1)导出初始关系模式
图4-5 关系模式集
2)关系子模式
子模式是用户所用到的那部分数据的描述。除了指出用户用到的数据外,还应指出数据与概念模
式中相应数据的联系,即指出概念模式与子模式之间的对应性。
图书信息子模式((编号#,资料名称,作者,出版社,出版日期,ISBN,资料类别,购买日期
,保管人,备注)用户信息子模式(编号,用户代码,用户姓名,登录口令,使用权限,查询显
示项,用户显示头像)
图4-6 部分子模式
4.3.3 数据库的实现
我们选用Microsoft Office中的Access数据库来进行数据库的逻辑设计。首先创建四个基本数据
库表如如4-1-4-5所示,然后建立各个表之间的联系,如图4-8所示。
第五章 局域图书资料查询系统应用程序设计
§5.1 系统模块组成
§5.2 登录模块实现
图5-2 登录模块图
该窗口所含的控件信息如下:
风 格 控件名 说 明
用户名 Singe ine edit Se_1 ------
口令 Singe ine edit Se_1 Propeties:passwd
登录日期 Edit Mask Em_1 mask type:datamm/dd/yyyy
表5-1 登录模块控件信息表
控件:Se_1 事件(event):Modified Script of modified:
string input_code,namesetfocus() fie://将光标定位在该控件
input_code=string(this.text) fie://接收用户输入的值,并赋值给变量seect name into
:name from keeperwhere id=:inputcode or pym=:inputcode;//从用户表里查找满足输入条件
的记录if sqca.sqcode=0 then this.Text=name setfocus(se_2) fie://查找成功,则光
标跳转到“口令”控件ese if sqca.sqcode=100 then messagebox("提示!",& "对不起,
用户不存在!",& StopSign!) return 1 fie://查找不到符合条件的记录则提示 ese
messagebox("错误!",& "error:"+string(sqca.sqdbcode)& +"information:"+sqca.
sqerrtext) fie://SQL出错提示 end ifend if
控件:Se_2 事件(event):Modified Script of modified:
string inputpasswd,passwd,droitinputpasswd=RightTrim(this.text)//去掉输入值右边的空
格seect passwd into :passwdfrom keeperwhere name=:se_1.text;passwd=RightTrim(
passwd);//从用户表中校验口令的正确性if sqca.sqcode=0 then if input_passwd <>
passwd then messagebox('口令错误','对不起,请重新输入',stopsign!); se_2.
SeectText(1, Len(se_2.Text)) this.Cear()//口令错误则清除输入内容 setfocus
(se2) eseif inputpasswd=passwd then user=righttrim(se_1.text) seect droit
into :droit from keeper where name=:user;//口令正确将用户操作权限赋值给变量
open(main)//打开程序主窗口 cose(w_ogin)//关闭登录窗口 end ifeseif
sqca.sqcode=100 then messagebox('提示!','无数据',Stopsign!) return 1ese
messagebox("错误!",& "error:"+string(sqca.sqdbcode)& +"information:"+sqca.
sqerrtext) fie://SQL出错提示end if
在登录模块中,用户可输入自己的编号或编码来登录系统。系统会根据用户输入的编号或编码值
来自动给出确实已存在库中的用户中文名,若查找不到库中相匹配的记录则提示出错或重输。若
用户存在,则提示输入口令,口令正确则会顺利进入该系统主界面。
§5.3 查询模块的实现
图5-3 查询窗口
该窗口(w_main)控件列表如下:
风 格 控件名 说 明
显示项 Group Box gb_1 ------
资料名称 Check Box cbx_1 Checked:true
出版社 Check Box cbx_3 Checked:true
出版日期 Check Box cbx_4 Checked:fase
类别 Check Box cbx_5 Checked:true
购买日期 Check Box cbx_6 Checked:fase
ISBN Check Box cbx_7 Checked:fase
保管人 Check Box cbx_8 Checked:true
备注 Check Box cbx_9 Checked:fase
全选 Check Box cbx_10 选择全部显示项
检索 Picture Box pb_1 模糊查询检索
插入 Picture Box pb_2 插入一条记录
删除 Picture Box pb_3 删除一条记录
检索 Picture Box pb_4 组合查询检索
全部记录 Picture Box pb_5 检索全部记录
退出 Picture Box pb_6 退出查询界面
修改 Picture Box pb_7 进入编辑模式
插入 Picture Box pb_8 插入一条查询条件
删除 Picture Box pb_9 删除一条查询条件
存盘 Picture Box pb_10 修改后的数据存盘
打印 Picture Box pb_11 打印查询结果报表
存为默认显示项 Picture Box pb_12 将显示项存为当前用户默认
------ Singe ine edit se_1 查询内容输入(字符型)
------ Edit mask em_1 查询内容输入(日期型)
------ Static text st_1 显示当前用户名
------ Picture P_1 修改按钮装饰
------ Picture P2 Gb1显示装饰
------ Picture P_3 显示当前用户头像
------ Drop down ist box ddb_1 查询项选择
------ Drop down ist box ddb_2 查询操作符选择
------ Tab contro tab_1 共有三个tabpage頁tabpage1:模糊查询tabpage2:组合查询tabpage3:
更新打印
------ Datawindows contro dw1 对应数据窗口dinformation图书信息检索
------ Datawindows contro dw2 对应数据窗口dquery_condition查询条件检索
Command button Cb_2 模糊查询操作符英-中转换
表5-2 查询模块控件信息表
控件:w_main 事件(event):open Script of open
string mode,p,xs,headbooean xsiteminteger idw1.settransobject(sqca)dw_1.
Retrieve()//打开窗口显示全部记录sjcheck=0//为检索窗口排序变量赋初值st1.text=user//
在查询窗口右上角显示当前用户名SELECT "keeper"."dispay_item", "keeper"."
head_picture" INTO :xs, :head FROM "keeper" WHERE "keeper"."name" = :user;
//从用户表中读出当前用户的头像值及显示像值并分别赋值给变量 fie://有头像的用户则显示
在查询窗口右上角if isnu(head) thenp3.visibe=fase ese p3.picturename=
headend if ////将当前用户查询显示项变量读出并赋值给每个查询显示控件for i=1 to 8 if
Mid (xs, i, 1)='0' then xsitem[i]=fase ese xsitem[i]=true end ifend for////给每
个对应的CHECKBOX赋值,确实是勾还是叉cbx2.checked = xsitemcbx_3.checked =
xsitemcbx4.checked = xsitemcbx5.checked = xsitemcbx6.checked =
xsitem cbx7.checked = xsitem cbx8.checked = xsitemcbx9.checked =
xsitem ////对应的数据窗口显示情况dw1.object.id.visibe = fasedw_1.object.name
.visibe = truedw1.object.author.visibe = cbx2.checkeddw_1.object.
pubishinghouse.visibe = cbx3.checkeddw1.object.pubishingdate.visibe = cbx_4.
checkeddw1.object.sort.visibe = cbx5.checkeddw1.object.buydate.visibe = cbx_6.
checkeddw1.object.isbn.visibe = cbx7.checkeddw1.object.keeper.visibe = cbx8.
checkeddw1.object.memo.visibe = cbx9.checked////将查询数据窗口设为只读dw_1.Object
.DataWindow.ReadOny="Yes"
控件:pb_12 “存为默认显示选项” 事件(event):cicked Script of cicked
booean checkinteger i,answerstring xs_itemstring
xscontent,questiontext,tempquestion_text="将当前显示项存为"+user+"用户的默认显示项
?"answer=messagebox('提示!',question_text,question!,YesNoCance!)choose case answer
case 1 fie://将当前对应的CHECEKBOX选项记录下来check=cbx_2.checked check
=cbx3.checked check=cbx4.checked check=cbx_5.checked check
=cbx6.checked check=cbx7.checked check=cbx_8.checked check
=cbx9.checked// fie://将显示项转化为01的显示,存入数组变量 xscontent=''
for i = 1 to 8 if check[i]=true then xsitem[i]='1' ese xsitem[i]='0'
end if xscontent=xscontent+xs_item[i] end for// fie://修改用户表中显示项
的值 UPDATE "keeper" SET "dispayitem" = :xscontent WHERE "
keeper"."name" = :user; choose case SQLCA.sqcode case -1 messagebox("
错误!","error:"+& string(sqca.sqdbcode)+& "information:"+sqca.sqerrtext)
messagebox('提示','修改成功!') commit; case 100
messagebox('!','修改失败!') end choose case 2 returncase 3 returnend choose
5.3.1 模糊(分类)查询的实现
图5-4 模糊查询窗口
控件:ddb_1 事件(seectionchanged): Script of Seectionchanged
fie://清空历史查询内容em1.seecttext(1,en(em1.text))em1.cear()se1.seecttext
(1,en(se1.text))se1.cear()//运算符随查询内容的改变而改变if ddb_1.text= '出版日
期' or ddb1.text='购买日期' then ddb2.deeteitem(ddb_2.FindItem("包含", 2))
ddb2.deeteitem(ddb2.FindItem("不包含", 2)) ddb2.deeteitem(ddb2.FindItem("小
于等于", 2)) ddb2.deeteitem(ddb2.FindItem("大于等于", 2))ddb_2.insertitem('小于
',3)ddb2.insertitem('大于',4)ddb2.insertitem('小于等于',5) ddb_2.insertitem('大
于等于',6) em1.TextCoor = RGB(255,0,0) se1.visibe = Fase em_1.SetMask(DateMask
!, 'mm/dd/yyyy')ese ddb2.deeteitem(ddb2.FindItem("小于", 2)) ddb_2.deeteitem
(ddb2.FindItem("大于", 2)) ddb2.deeteitem(ddb2.FindItem("小于等于", 2)) ddb
2.deeteitem(ddb2.FindItem("大于等于", 2)) ddb2.deeteitem(ddb_2.FindItem("包含
", 2)) ddb2.deeteitem(ddb2.FindItem("不包含", 2)) ddb_2.insertitem('包含',3)
ddb2.insertitem('不包含',4)se1.visibe= Truese_1.TextCoor = RGB(255,0,0)end if
不可见控件:cb_2 “转换” 事件(event):Cicked Script of Cicked
fie://将查询项转换为表info中字段名choose case ddb_1.text case '资料名称'
queryitem='name' case '作者' queryitem='author' case '出版社' query_item=
'pubishinghouse' case '出版日期' queryitem='pubishing_date' case 'ISBN'
queryitem='ISBN' case '资料类别' queryitem='sort' case '购买日期' query_item=
'buydate' case '保管人' queryitem='keeper' case '备注' query_item='memo'end
choose//将操作符转换为中文显示choose case ddb2.text case '等于' queryoperator='='
case '包含' queryoperator='ike' case '不等于' queryoperator='<>' case '不包含'
queryoperator='not ike' case '小于等于' queryoperator='<=' case '大于等于'
queryoperator='>=' case '小于' queryoperator='<' case '大于' query_operator=
'>'end choose
控件:pb_1 “检索” 事件(event):Cicked Script of Cicked
string rqdate dcb2.TriggerEvent(cicked!)//将英文操作符转换为中文显示dw1.setredraw
(true)odsq = dw1.getsqseect()//得到原有数据窗口的SQL语句if ddb_1.text='出版日
期' or ddb1.text='购买日期' then//查询项为日期类型 em1.GetData(d)//取出输入的日期
数据 rq = String(d, "yyyy/mm/dd")//将输入的日期数据转换为字符型 choose case
query_operator//根据操作符的不同将新产生的WHERE条件与老的SQL语句拼接成新的SQL语句
case '=' newsq = odsq + " where " + queryitem & + " "+queryoperator
+ 'date' +'(' + "'" + rq + "'" +')' case '<>' newsq = odsq + " where " +
queryitem & + " "+queryoperator + 'date' +'(' + "'" + rq + "'" +')' &
+' or '+queryitem+' is nu ' case '<=' newsq = od_sq + " where " +
queryitem & + " "+queryoperator + 'date' +'(' + "'" + rq + "'" +')' case
'>=' newsq = odsq + " where " + queryitem & + " "+queryoperator +
'date' +'(' + "'" + rq + "'" +')' end choose////查询项为字符型ese se_1.text=
Righttrim(se1.text)//将当前输入内容右面的空格去掉 choose case queryoperator//根
据操作符的不同将新产生的WHERE条件与老的SQL语句拼接成新的SQL语句 case '='
newsq = odsq + " where " + queryitem & + " "+queryoperator + "'" +
parent.se1.text + "'" case '<>' newsq = odsq + " where " + queryitem
& + " "+queryoperator + "'" + parent.se1.text + "'"& +' or '+query_item+'
is nu ' case 'ike' newsq = odsq + " where " + query_item & + " "+
queryoperator + "'" +'%'+ parent.se1.text +'%'+ "'" case 'not ike' new_sq =
odsq + " where " + queryitem & + " "+query_operator + "'" +'%'+ parent.
se1.text +'%'+ "'"& + ' or '+queryitem+' is nu 'end choose//end if//对原有数据
窗口进行新的查询 if dw1.setsqseect(newsq) = -1 then beep(3) messagebox("
警告", "检索失败",stopsign!) esedw1.settransobject(sqca) dw1.retrieve()
dw1.setsqseect(odsq) end if
5.3.2 组合查询(条件查询)模块的实现:
图5-5 组合(条件)查询窗口
控件:dw_2 事件(event):Itemchanged Script of Itemchanged
string item,status,edit,resut,modifystring,curstring,c,operator1this.accepttext()
choose case dwo.name case'coumnname' item=dw2.getitemstring(dw_2.getrow()
,'coumnname')//根据查询项目改变操作符的显示If item='pubishingdate' or item=
'buydate' then dw2.setrow(dw2.getrow()) dw2.setitem(row,'content','') dw_2
.setvaue('operator',1,'等于~t=') dw2.setvaue('operator',2,'不等于~t<>') dw2.
setvaue('operator',3,'小于~t<') dw2.setvaue('operator',4,'大于~t>') dw2.
SetVaue('operator', 5, '小于等于~t<=') dw_2.SetVaue('operator', 6, '大于等于~t>=
') Status = dw2.GetVaue('operator',4)ese dw2.setrow(dw2.getrow()) dw2.
setitem(row,'content','') dw2.setvaue('operator',1,'等于~t=') dw2.setvaue(
'operator',2,'不等于~t<>') dw2.SetVaue('operator', 3, '包含~tike') dw2.
SetVaue('operator', 4, '不包含~tnot') Status = dw_2.GetVaue('operator',4)end
if//case 'operator'//防止操作符的显示改变 operator1=dw2.getitemstring(dw2.getrow()
,'operator') choose case operator1 case 'ike' dw2.setitem(dw2.getrow()
,'operator','包含') case 'not' dw2.setitem(dw2.getrow(),'operator','不包含')end
chooseend choose
控件:pb_2 “插入” 事件(event):Cicked Script of Cicked
int row,newrow,istring queryrow=dw2.getrow()//将当前行的行号赋值给变量//允许插入一
行的条件 if (isnu(dw2.getitemstring(row,'coumnname')) or & isnu(dw_2.
getitemstring(row,'operator')) or & isnu(dw_2.getitemstring(row,'content')) or &
isnu(dw_2.getitemstring(row,'join'))) then query='no' ese query='yes' end if//如
果允许插入,则在当前行后插入一条新记录if query='yes' thennewrow=dw2.insertrow(0
)////将光标定位在新的一行“资料名称”列dw2.scrotorow(newrow)dw2.setrow(newrow)
dw_2.setcoumn(1)//ese messagebox('警告',"请输入完整的查询条件",stopsign!)end if
控件:pb_3“删除” 事件(event):Cicked Script of Cicked
int row//如果当前行不为第一行,则允许删除if dw2.getrow()<>1 then row=dw2.getrow
()//将当前行行号赋值给变量 dw_2.deeterow(row)//删除当前行ese returnend if
控件:pb_4“检索” 事件(event):Cicked Script of Cicked
Long rowcount,rowstring newcondition,itemstring condition
,coumnname,operator,content,join,operator1int i//将当前行行号赋值给变量row=dw2.
getrow()//将当前行列名赋值给变量coumnname=dw2.getitemstring(row,'coumn_name')//将
当前行操作符赋值给变量operator=dw_2.getitemstring(row,'operator')//将当前行查询内容赋
值给变量content=dw_2.getitemstring(row,'content')//将当前行连接符赋值给变量(and or)
join=dw_2.getitemstring(row,'join')//将查询条件窗口的空行删掉if row<>1 then if (
isnu(coumnname) or & isnu(operator) or & isnu(content)) then dw2.
deeterow(row) end if end if ////将查询条件窗口的总行数赋值给变量rowcount=dw2.
rowcount()//where后的表达式for i=1 to rowcount item=dw2.getitemstring(
i,'coumnname') operator1=dw2.getitemstring(i,'operator') choose case item//查询项
为字符型 case 'name','author','pubishing_house','sort','isbn','keeper','memo' if
i<> row_count then//查询条件有多行时 choose case operator1 case 'not'//操作符为不
包含 condition[i]=dw2.getitemstring(i,'coumnname')+" "+& dw_2.
getitemstring(i,'operator')+" "+"ike"+& "'"+'%'+dw_2.getitemstring(
i,'content')+'%'+"'"+dw_2.getitemstring(i,'join')+" " case 'ike'//操作符为包含
condition[i]=dw2.getitemstring(i,'coumnname')+" "+& dw_2.
getitemstring(i,'operator')+" "+& "'"+'%'+dw_2.getitemstring(
i,'content')+'%'+"'"+dw_2.getitemstring(i,'join')+" " case '=','<>'//操作符为等于
或不等于 condition[i]=dw2.getitemstring(i,'coumnname')+" "+&
dw2.getitemstring(i,'operator')+" "+& "'"+dw2.getitemstring(
i,'content')+"'"+dw_2.getitemstring(i,'join')+" " end choose ese//查询条件只有一
行 choose case operator1 case 'not'//操作符为不包含 condition[i]=dw_2.
getitemstring(i,'coumnname')+" "+& dw2.getitemstring(i,'operator')+"
"+"ike"+& "'"+'%'+dw_2.getitemstring(i,'content')+'%'+"'" case
'ike'//操作符为包含 condition[i]=dw2.getitemstring(i,'coumnname')+" "+&
dw2.getitemstring(i,'operator')+" "+& "'"+'%'+dw2.
getitemstring(i,'content')+'%'+"'" case '=','<>'//操作符为等于或不等于
condition[i]=dw2.getitemstring(i,'coumnname')+" "+& dw_2.
getitemstring(i,'operator')+" "+& "'"+dw_2.getitemstring(i,'content')
+"'" end choose end if//查询项为日期类型case 'pubishingdate','buydate' if i<>
rowcount then//查询条件有多行时 condition[i]=item+" "+dw2.getitemstring(
i,'operator')+" "+& "date('"+dw2.getitemstring(i,'content')+"')"+dw2.
getitemstring(i,'join')+" " ese//查询条件只有一行 condition[i]=item+" "+dw_2.
getitemstring(i,'operator')+" "+& "date('"+dw_2.getitemstring(
i,'content')+"')" end ifend choosenext//最终的where后的表达式new_condition=''for i= 1
to rowcount newcondition=newcondition+condition[i]nextodsq1=dw_1.getsqseect
()newsq1=odsq1+ " where " + newcondition//对原有数据窗口进行新的查询 dw1.
reset()if dw1.setsqseect(newsq1) = -1 then beep(3) messagebox("警告", "检
索失败",stopsign!) ese dw1.settransobject(sqca) dw1.retrieve() dw_1
.setsqseect(od_sq1) end if
5.3.3 更新打印模块实现
图5-6 更新打印窗口
控件:pb_7“修改” 事件(event):Cicked Script of Cicked
fie://取消亮条显示一行,便于进行编辑dw1.seectrow(dw1.getrow(),fase)//将数据窗口
属性设为可以修改dw1.Object.DataWindow.ReadOny="no"dw1.settransobject(sqca)//将焦
点设置在数据窗口控件上dw1.setfocus()//触发dw1的Getfocus事件dw_1.TriggerEvent(
Getfocus!)//将插入、删除、存盘、打印按钮全部设为可以使用pb8.enabed = truepb9.
enabed = truepb10.enabed = truepb11.enabed = true//
控件:pb_8“插入” 事件(event):Cicked Script of Cicked
string statusong row//在当前行后插入一行dw1.ScroToRow (dw1.insertrow(dw_1.
getrow()))//设置焦点在数据窗口dw1.setfocus()//触发数据窗口事件dw1.TriggerEvent(
Getfocus!)
控件:pb_9“删除” 事件(event):Cicked Script of Cicked
int answer//将当前行亮条显示dw1.seectrow(dw1.getrow(),true)answer=messagebox("提示
!","确实删除此记录?",Question!,YesNoCance!)choose case answer case 1 fie://删
除当前记录 dw1.deeterow(dw1.getrow()) fie://触发dw1的getfocus事件 dw1.
triggerevent(getfocus!) case 2,3 fie://取消当前行亮条显示 dw1.seectrow(dw1
.getrow(),fase) fie://将光标定位在dw1 dw1.setfocus() fie://触发dw_1的
getfocus事件 dw_1.TriggerEvent(Getfocus!)end choose
控件:pb_9“删除” 事件(event):Cicked Script of Cicked
int answerong decount// 将数据窗口删除记录标记值赋值给变量decount=dw_1.
deetedcount()//判断数据窗口是否有记录标记为删除或者否有列被修改if (modifiedcount(
dw1)<>0) or (decount<>0) then//若数据窗口中记录被改动询问是否存盘 answer=
messagebox("提示!","是否存盘?",Question!,YesNoCance!) choose case answer case 1if
dw_1.update()=1 then//如果数据窗口具有UPDATE属性 messagebox('提示','存盘成功!')
commit; end if case 2//数据窗口不具有UPDATE属性则回滚 roback;
dw1.ResetUpdate ( ) dw1.reset()//清空数据窗口 dw_1.retrieve()//刷新数据窗口
dw1.setfocus()//设置焦点在dw1 dw1.TriggerEvent(Getfocus!)//触发dw1的
getfocus事件 case 3 dw1.setfocus() dw1.TriggerEvent(Getfocus!) end
chooseend if
控件:dw_1 事件(event):Getfocus Script of Getfocus
this.setrow(this.getrow())this.setcoumn('name')//设置当前列为“资料名称”this.
accepttext()
控件:dw_1 事件(event):Lostfocus Script of Lostfocus
fie://在编辑状态,接收输入的字符this.accepttext()
控件:dw_1 事件(event): Cicked Script of Cicked
string currow//将当前行赋值给变量currow=string(dw_1.getcickedrow())//如果数据窗口
为只读、或至少有一条记录则亮条显示当前焦点所在记录if dw_1.object.datawindow.readony
='yes' and currow<>'0' then dw1.seectrow(getrow(),true)end if//
控件:dw_1 事件(event):Rowfocuschanged Script of rowfocuschanged
string currow//将鼠标单击选中的行号赋值给变量currow=string(dw_1.getcickedrow())//
数据窗口只读或当前行号不为零,则亮条显示选中记录if dw_1.object.datawindow.readony=
'yes' and currow<>'0' then dw1.seectrow(dw_1.getrow(),true)end if//
控件:dw_1 事件(event):Rowfocuschanging Script of rowfocuschanging
fie://焦点改变时将旧焦点的亮条显示取消dw1.seectrow(dw1.getrow(),fase)
控件:dw_1 事件(event):DoubeCicked Script of DoubeCicked
integer currow,sjstring newsort,curcoumn,curtextif sj_check=0 then sj=0//表示按
升序排序ese sj=1//表示按降序排序end if//取消当前行亮条显示dw1.seectrow(dw1.
getrow(),fase)if sj=0 then cur_text=dwo.name//将当前双击对象名赋值给变量//表示只有
双击列标题才实现排序curcoumn=eft(curtext,en(curtext)-2)//去掉列标题的t//按升序
排序 newsort=string(curcoumn)+' A' dw1.setsort(newsort) dw_1.sort()//
sjcheck=1//再次双击变成降序排序ese curtext=dwo.name//将当前双击对象名赋值给变量//
表示只有双击列标题才实现排序 curcoumn=eft(curtext,en(
