摘要:
本文结合日常图书查询系统的实际需要,通过对C/S模式、PowerBuilder6.5开发工具、数据库以及SQL语言的深入学习及实践,主要完成了局域网图书资料查询系统的需求分析、数据库设计、应用程序设计的工作。
首先,在绪论部分介绍了局域网图书资料查询系统的应用背景、开发环境以及选用的开发工具与数据库的关系,阐明了局域网的概念。并对数据库的体系结构、DBMS进行了介绍。
第二章的开始介绍了关系型数据库的基本概念,着重说明了几个关键概念的定义;然后对SQL语言作了一个介绍说明;最后通过两个例子介绍了PB6.5用PowerScript语言调用SQL的方式。
第三章从特点和功能入手,介绍了开发工具PowerBuilder6.5;并且介绍了C/S模式的概念、特点以及C/S模式与开发工具PowerBuilder6.5的联系;在这个章节的最后简单介绍了PB6.5对数据库的操作。
第四章用软件工程的方法分析了局域网图书资料查询系统,对整个系统进行了需求分析、功能模块划分,并通过ER图对数据库进行概念设计、用Microsoft Access对数据库进行逻辑设计。
第五章是对局域网图书资料查询系统的具体设计。描述了整个系统详细的功能模块划分,描述了登录模块、模糊(分类)查询、多条件(组合)查询、数据编辑更新模块以及数据维护模块的实现过程,并对设计源代码进行了注释分析。
设计充分利用PowerBuilder6.5的PowerScript语言对SQL语言的操作特性,灵活运用数据窗口技术、以及PB的控件技术等,提高了程序设计质量。
关键词:C/S结构,局域网,数据库,SQL语言,PowerBuilder6.5
目录:
摘 要 ………………………………………………………………………………… I
第一章 绪 论 ……………………………………………………………………… 1
§1.1 数据库应用系统开发简介 ………………………………………………… 1
§1.2 局域网图书资料查询系统……………………………………………………3
§1.3本文所作工作 …………………………………………………………… 4
第二章 数据库理论基础 …………………………………………………………… 6
§2.1 关系型数据 ……………………………………………………………… 6
§2.2 SQL语言介绍 ……………………………………………………………… 9
§2.3 PB6.5对数据库的操作 ………………………………………………………11
第三章 PB 6.5及其数据库编程……………………………………………………… 13
§3.1开发工具PowerBuilder6.5……………………………………………………13
§3.2 PowerBuilder与Client/Server体系结构………………………………………16
§3.3 PowerBuilder6.5数据库编程 ……………………………………………… 18
第四章 局域网图书资料查询系统设计分析 …………………………………………21
§4.1 应用需求分析 ……………………………………………………………… 22
§4.2 系统功能模块划分 ………………………………………………………… 23
§4.3 系统数据库设计 …………………………………………………………… 24
第五章 局域网图书资料查询系统应用程序设计 …………………………………29
§5.1 系统模块组成 ………………………………………………………… 29
§5.2登录模块的实现……………………………………………………… 30
§5.3查询模块的实现 …………………………………………………………… 33
5.3.1模糊(分类)查询的实现 ………………………………………………39
5.3.2组合(条件)查询的实现 ………………………………………………44
5.3.3更新打印模块的实现 ………………………………………………… 51
§5.4 数据维护模块的实现…………………………………………………………56
第六章 结束语 ……………………………………………………………………… 63
致 谢 ………………………………………………………………………………… 64
参考书目 ……………………………………………………………………………… 65
第一章 绪 论
§1.1 数据库技术
数据库技术作为数据管理技术,是计算机软件领域的一个重要分支,产生于60年代末。现已形成相当规模的理论体系和实用技术。优秀的数据库设计是应用成功的基石。万万丈高楼平地起,数据库设计如同高楼的基石,是开发高品质应用的前提。
1.1.1 数据库的体系结构
数据的体系结构分成三级:内部级(Internal),概念级(Conceptual)和外部级(External)。这个三级结构有时也称为“三级模式结构”。
l 外部级:最接近用户,是单个用户所能看到的数据特性。单个用户使用的数据视图的描述称为“外模式”。
l 概念级:涉及到所有用户的数据定义、是全局的数据视图。全局视图的描述称为“概念模式”。
l 内部级:最接近于物理存储设备,涉及到实际数据存储的结构物理存储数据视图的描述称为“内模式”。
数据库的三级模式结构是数据的三个抽象级别。它把数据的具体组织留给DBMS
去做,用户只要抽象地处理数据,而不必关心数据在计算机中的表示和存储,这样就减轻了用户使用系统的负担。
1.1.2 数据库管理系统(DBMS)
数据库管理系统(DBMS)是指数据库系统中管理数据的软件系统。DBMS是数据库系统的核心组成部分。对数据库的一切操作,包括定义、查询、更新及各种控制,都是通过DBMS进行的。
在不同的计算机系统中,由于缺乏统一的标准,即使同种数据模型的DBMS,它们在用户接口、系统功能方面也常常是不相同的。
用户对数据库进行操作,是由DBMS把操作从应用程序带到外部级、概念级、再导向内部级,进而操作存储器中的数据。DBMS的主要目标,是使数据作为一种可管理的资源处理。
DBMS的主要功能为:
l 数据库定义功能:DBMS提供数据定义语言(DDL)定义数据库的三级结构,包括外模式、概念模式、内模式及基相互之间的映象,定义数据的完整性、安全控制等约束。因此,在DBMS中应包括DDL的编译程序。
l 数据库的操纵功能:DBMS提供数据操纵语言(DML)实现对数据库中数据的操作。基本的数据操作分成两类四种:
² 检索(查询)
² 更新(插入、删除、修改)
l 数据库的保护功能:数据库中的数据是信息社会的战略资源,对数据的保护是至关重要的大事。DBMS对数据库的保护主要通过四个方面实现:
² 数据库的恢复:在数据库被破坏或数据不正确时,系统有能力把数据库恢复到正确的状态。
² 数据库的并发控制:DBMS的并发控制子系统能防止错误发生,正确处理好多用户、多任务环境下的并发操作。
² 数据库的完整性控制:保证数据库中数据及语义的正确性和有效性,防止任何对数据造成错误的操作。
² 数据库的安全性控制:防止未经授权的用户蓄谋或无意地存取数据库中的数据,以免数据的泄露、更改或破坏。
² 数据库的存储管理:把各种DML语句转换成低层的文件系统命令,起到数据的存储、检索和更新的作用。
² 数据库的维护功能:它有许多实用程序提供给数据库管理员:
Ø 数据装载程序
Ø 备份程序
Ø 文件重组织程序
Ø 性能监控程序
² 数据字典:数据库系统中存放三级结构定义的数据库称为数据字典(DD)。对数据库的操作都要通过访问DD才能实现,通常DD中还存放数据库运行时的统计信息。
1.1.3 PowerBuilder与数据库
nPowerBuilder与数据库是“配合”与“协调”的关系
PowerBuilder是客户/服务器体系结构下客户端的开发工具,用于开发客户应用程序。这个程序首先建立一个与数据库的通信渠道,然后将用户的需求以某种方式传送给数据库服务器。在应用程序接收到数据库服务器返回的数据后,它分析返回的数据并呈现给用户。因此我们说,客户应用程序只完成请求和表现数据的工作,是用户操作计算机的人机界面,大多数数据处理是由服务器完成的。
数据库数据器是一个存取数据和管理数据的软件,它针对客户的请求为客户提供数据服务。这些服务包括数据插入、修改和查询等。客户对数据库服务器提出请求用的语言是SQL(Strucrured Query Language)。SQL是大多数数据库服务器使用的查询语言。
因此我们说,PowerBuilder与数据库的关系是“配合”与“协调”的关系。PowerBuilder完成数据请求、数据表现、菜单、界面等表象方面的工作,而数据库服务器完成数据库的存储管理、并发控制、事务管理、完整性维护、查询优化等工作。
§1.2 局域网图书资料查询系统
1.2.1 局域网(LAN)
计算机网络是指将多台具有独立功能的计算机,通过通信线路和通信设备连接起来,在网络软件的支持下实现数据通信和资源共享的计算机系统。
计算机网络的规模有大有小,大的可以覆盖全球,小的仅局限于一个办公室。现在一般按照网络覆盖的地理范围将计算机网络分为三类:局域网(LAN)、城域网(MAN)、广域网(WAN)。
局域网是覆盖范围在10公里以内的计算机网络。局域网传输速度快,一般局限在一个单位内部,例如一所学校或一家企业。
1.2.2 应用程序开发背景
一个数十人的科研或开发小组,搜集了上千册有用的图书资料,其中有一部分分布在个人手中,为了方便大家查阅,需要对每本书的状态进行跟踪,另新进和丢失的图书资料必须得到及时的反映。
1.2.3 系统功能
l 图书分类查询,多条件查询,模糊查询
l 用户必须登录方能执行各种操作,允许用户在客户机或浏览器修改,添加
删除图书资料,实行松散管理,这建立在用户高度自觉的基础之上,当然也可由管理员在服务器上直接对数据库操作。
l 能将查询结果生成报表,并打印输出。
1.2.4 系统运行环境
该系统采用Client/Server模式进行设计:局域网中有一台服务器,其上运行服务器程序,操作系统为windows2000 server,客户机操作为Windows98,其上运行客户端程序。
1.2.5 系统开发工具
该系统采用PowerBuilder6.5进行开发,数据库服务器端为SQL Anywhere5.0 Server;客户端则为SQL Anywhere client,整个系统在SQL Anywhere Local端调试完成。
§1.3 本文所作工作
首先,在绪论部分介绍了局域网图书资料查询系统的应用背景、开发环境以及选用的开发工具与数据库的关系,阐明了局域网的概念。并对数据库的体系结构、DBMS进行了介绍。
第二章的开始介绍了关系型数据库的基本概念,着重说明了几个关键概念的定义;然后对SQL语言作了一个介绍说明;最后通过两个例子介绍了PB6.5用PowerScript语言调用SQL的方式。
第三章从特点和功能入手,介绍了开发工具PowerBuilder6.5;并且介绍了C/S模式的概念、特点以及C/S模式与开发工具PowerBuilder6.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表示:
2. 键(KEY)键由一个或几个属性组成,在实际使用中,有下列几种键:
1) 超键(Super Key):在关系中能惟一标识元组的属性集称为关系模式的超键。
2) 候选键(Candidate Key):不含有多余属性的超键称为候选键。也就是在候选键中,若要再删除属性,就不是键了。
3) 主键:(Primary Key):用户选作元组标识的一个侯选键称为主键。一般,如不加说明,则键是指主键。
3. 关系的定义和性质
我们可以用集合的观点定义关系。关系是一个元数为K(K>=1)的元组的集合。
把关系看成是一个集合,集合中的元素是元组,每个元组的属性个数应相同。在关系模型中,对关系作了下列规范性限制:
1) 关系中每一个属性值都是不可分解的。
2) 关系中允许出现相同的元组(没有重复元组)
3) 由于关系是一个集合,因此不考虑元组间的顺序,即没有行序。
4) 元组中,属性在理论上也是无序的,但在使用时按习惯考虑列的顺序。
2.1.2数据库的设计理论
关系数据库的设计理论主要包括三个方面的内容:数据依赖、范式,模式设计方法。其中数据依赖起着核心的作用。
1. 函数依赖(Functional 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 normal form,简称1st NF)就是指在同一表中没有重复项出现,如果有则应将重复项去掉。这个去掉重复项的过程就称之为规范化处理。在本文所讨论的开发方法里,1st NF实际上是没有什么意义的。因为我们按规范化建立的指标体系和表的过程都自动保证了所有表都满足1st NF。
第二范式(second normal form,简称 2nd NF)是指每个表必须有一个(而且仅一个)数据元素为主关键字(primary key),其它数据元素与主关键字一一对应。例如,在图l9.7中如果我们将合同号定义为主关键字(其它数据元素中的记录数据都有可能重名,故不能作为主关键字),故只要知道了一个合同记录的合同号,就可以唯一地在同一行中找到该合同的任何一项具体信息。通常我们称这种关系为函数依赖(functional depEndence)关系。即表中其它数据元素都依赖于主关键字,或称该数据元素唯一地被主关键字所标识。
第三范式(third normal form,简称 3rd NF)就是指表中的所有数据元素不但要能够唯一地被主关键字所标识,而且它们之间还必须相互独立,不存在其它的函数关系。也就是说对于一个满足了 2nd NF的数据结构来说,表中有可能存在某些数据元素依赖于其它非关键宇数据元素的现象,必须加以消除。
为防止数据库出现更新异常、插入异常、删除异常、数据冗余太大等现象,关系型数据库要尽量按关系规范化要求进行数据库设计。下面以教务管理信息系统为例来进行分析。
3. 模式设计方法
一个好的模式设计方法应符合下列三条原则:
l 表达性:涉及到两个数据库模式的等价性问题,即数据等价和依赖等价,分别用无损联接和保持函数依赖来衡量。
l 分离性:是指属性间的“独立关系”应该用不同的关系模式表达。独立联系是我们所考虑的“基本信息单位”。实际上分离就是清除存储异常和数据冗余现象。如果能达到这个目的,就分离。分离的基准就是一系列范式,分离与依赖等价有时是不可兼容的。
l 最小冗余性:要求在分解后的数据库能表达原来数据库的所有信息这个前提下实现。目的就是节省存储空间,提高对关系的操作效率,清除不必要的冗余。但要注意,在实际使用中,并不一定要达到最小宙余。因为有时带点冗余对于查询处理是有好处的。
关系模式的方法基本上可以分为分解与合成两大类。分解型算法要求输入一个
初始模式集和依赖集,而结果满足数据等价要求。对于合成型算法只要求输入初始依赖集,结果满足依赖等要求。但它们依据的基本思想是共同的,即独立的联系独立表示。
§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对数据库的操作
PowerBuilder对数据库的操作即可以通过数据窗口完成(本质上是数据窗口把在屏幕上对数据库的操作转化成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 file://定义光标,光标名为C1
SELECT name FROM auths file://光标对应的SELECT语句
WHERE author_code like :V1; file://SELECT语句中用到了变量V1
OPEN C1; // 打开光标,此时执行此光标对应的SQL SELECT语句。
Lab1:
FETCH C1 INTO :name; file://取记录,光标下移一条
If SQLCA.SQLCode=0 then file://取记录成功
Goto lab1 file://如果成功取出记录,则取下一条
End if
打开光标的时候,变量V1被其值“A%”替换,因此最后执行的SQL SELECT语句为:
SELECT name FROM auths
WHERE author_code like ‘A%’
执行完这条语句后,把光标C1定位到了查询出的第一条记录上。每执行一次语句:
FECTCH C1 INTO :name ;
则取出一条记录送给变量name,然后光标移到下一条记录。如果想控制查询结果的次序,必须在SELECT语句中用ORDER BY子句,否则,没有其它的办法。我们不可能一下子把光标定位在某条记录上,只能从第一条开始,一个个地移。
第三章 PB6.5及其数据库编程
§3.1 开发工具PowerBuilder6.5
3.1.1 PowerBuilder6.5特点及功能
要适应企业环境不断变化的需求,成功地开发出高质量的应用系统,必须采用先进的应用开发工具。这对于减轻应用开发人员的开发负担,提高开发速度和质量都有十分重要的意义。Sybase公司推出的PowerBUilder6.0/6.5是用于Client/Server、Web及组件开发的企业级应用开发工具。它占全球开发工具市场近50%,是当前最优秀的开发工具之一。它具有以下优异的功能和特点:
1、内置的关系数据库
PowerBuilder本身带有一套数据库系统Sybase SQL Anywhere。这样做的好处是, PowerBuilder可以脱离网络数据库服务器独立运行,从而在开发阶段脱离网络服务器上的数据库。当然,利用这一功能也可以开发和调试单用户的独立的数据库应用。下图显示了内置数据库的工作原理:
如果没有内置的数据库Sybase SQL Anywhere,开发时则不能脱离网络服务器上的数据库(如图3-2所示):
2、数据窗口(Datawindow)对象
PowerBuilder拥有数据窗口这个具有专利技术的智能对象,利用该对象可以操作关系数据库的数据而无需编写SQL语句。用户可以查询、修改、插入、删除、浏览、打印、以多种文件格式打开和存储数据,或在数据窗口中直接定义功能按钮实现预定义的系统功能,如插入、删除数据的操作。它还支持数据库事务管理和并发控制等机制。其工作机理如下图所示:
3、丰富的数据窗口数据源和多种样式的数据显示格式
数据窗口可以用来维护数据和显示数据,可定义多种显示风格和数据显示格式;并且还可以与TreeView控制、ListView控制配合使用,创建出更丰富的数据显示格式
4、支持多种商业图形,包括多种类型的二维和三维的图形
5、支持组件的开发和调用
用PowerBuilder开发的组件,可以在其它应用中调用,可以由多种事务管理服务器管理。
6、具有面向对象的特征
PowerBuilder采用了面向对象的开方式,这可以使系统开发人员在无需精通专用语言的情况下就可以迅速转向面向对象的开发。PowerBuilder应用是由一系列对象组成的,包括窗口、菜单、函数、数据窗口和各种控制等对象,它支持对象的继承、封装和多态性。
7、有机结合的集成开发环境
8、完全支持Windows的窗口信息和控制
9、强有力的PowerScript编程语言
它能使开发人员很容易地将简单或复杂的事务逻辑与应用相配合。该语言还有几百个函数用于操纵对象、处理数字、文本、字符串、日期和应用分布,进行文件处理、报表打印,用DDE和OLE 2.0进行程序之间的通信,直接调用SQL语句操纵数据库等等。
10、PowerBuilder提供了多种流行软件的接口库
Netware Library
Pen Computing Library
Lotus Notes Library
Microsoft MAPI
11、PowerBuilder支持多种平台
目前,PowerBuilder能够在Microsoft Windows 3.X、Windows 95Windows NTAlpha/IntelApple Mac Sun Saloris IBM AIX HP Unix等多种平台上开发和运行应用程序,并能够不加改动地应用于其它平台上。
12、支持Internet/Intranet下的Web应用开发
13、支持团体开发
14、对多种数据库的支持
PowerBuilder几乎支持所有的数据库,它提供了到多种数据库的专用接口和ODBC接口。
§3.2 PowerBuilder与Client/Server体系结构
3.2.1 Client/Server模式
在C/S结构中,存在着几个非常重要的基本概念,它们是:主机、终端、客户机、工作站和服务器。在分析C/S网络结构之前,必须搞清楚它们之间的区别。
最早的计算机网络是伴随着主机(Host)和终端(Terminal)这两个概念的出现而产生的。当时的主机通常是指具有中央处理单元(CPU)的大型机或功能较强的小型机,而终端则是指计算机的输入输出设备。终端没有自己的CPU,当然也没有自己的内存,其主要功能是将键盘输入的请求数据发往主机并将主机的运算结果显示出来。主机和终端共同构成了集中式系统结构。在这种应用系统中,几乎所有的工作都是由主机来完成,终端仅仅作为一种输入输出设备,因此系统负荷重、效率低、扩充性差。
之后随着计算机网络结构的细化,不同的计算机开始在网络中担负不同的任务,于是出现了文件服务器/网络工作站(F/W)式结构的模型。其中,工作站(Workstation)和服务器(Server)都是独立的计算机。当一台连入网络的计算机向其它计算机(工作站)提供各种网络服务(如数据、文件的共享)时,它就被叫做服务器。而那些用于访问服务器资料的计算机则被叫做工作站。在F/W结构中,所有实际的数据处理工作仍在运行数据库应用程序的PC工作站上完成,因此不论文件服务器的性能有多高,其整体网络性能都将受到PC机能力的限制。
客户机(Client)是伴随C/S数据访问的兴起而被提出来的,在一般人的理解中它和F/W概念没有本质的区别。但是,严格说来,C/S模型并不是从物理分布的角度来定义的。它既包括具体的网络结构设计,又包括软件的运行和组织,所体现的是一种网络数据的访问方式。这里的客户机和服务器不仅指承担不同任务的计算机本身,而且包括主机上运行的客户端和服务器端的软件环境。它们的区别,是相对于各自在网络数据库访问中所处的地位或实现的功能而言的。
理解了以上的基本概念,才可以更好地理解C/S结构的实质和运行方式:所谓C/S结构,是将数据存取与应用程序分离开来,把一个软件系统或应用系统按功能分成若干个部分,再将这些软件的组成部分按其不同的角色分成Client软件和Server软件,分别放置在客户机和服务器上。客户机程序负责用户交互界面、数据表示及应用处理逻辑等应用部分,而服务器端则负责数据存取管理、完整性控制及并发控制等数据库管理部分。客户机程序应用通过SQL语句访问数据库,相应的SQL语句经网络传输到服务器端,由服务器端的数据库服务器解释执行这些SQL语句,执行后的结果数据送回客户机。
3.2.2 Client/Server体系结构的优缺点
我们看到,客户/服务器体系结构有以下优点:
l 应用逻辑与数据实现分离,实现了在网络上的负载均衡;
l 充分利用了网络服务器的处理能力。客户中需将请求送数据库服务器,数据查询工作由服务器来承担,服务器的能力可以得到充分的发挥。
但是它也有以下一些缺点:
l 由于计算机技术的快速进步和企业需求变化的加快,企业面临的硬件、网络操作系统、数据库系统、开发工具、应用系统的升级周期越来越短,因此“维护”客户服务器的费用就变得越来越高昂。
l 快速升级的硬件、网络操作系统、数据库系统、开发工具使企业的技术人员失去了方向:不断地消化新技术,却无瑕顾及企业要解决的问题
l 在客户端,必须安装操作系统(一般为Windows95/NT)、网络软件、特定的中间件(Sybasse Net-Library)以及应用软件系统才能工作,因此应用系统的安装、升级和维护通常需要专业人员才能用胜任,且必须各站点逐个安装,从而使客户端的维护费用变得也很高昂。对主机系统来说,这一点要优越的多,只需要在主机上安装一次即可。
3.2.3 PowerBuilder是客户/服务器体系结构下客户端的开发工具
前面已提到,PowerBuilder是客户/服务器体系结构下开发客户程序用的开发工具,用PowerBuilder开发出的程序可以存取数据库中的数据。下图表示了PowerBuilder与数据库配合工作的方式。
我们看到,PowerBuilder作为客户端的应用开发工具,主要完成的是表示逻辑方面的工作,例如,菜单、录入界面。而数据库服务器管理的是事务逻辑和数据存取方面的工作。因此,PowerBuilder开发工作应分为两部分,一部分是前台表示逻辑方面的工作,另一部分是数据库后台方面的设计工作(如数据库触发器、存储过程和视图等的设计)。一个好的应用系统,前台开发与后设计应是有机结合、合理分布的;良好的后台设计可以降低前台的开工作量,提高系统的运行效率。
§3.3 PowerBuilder6.5数据库编程
PowerBuilder与数据库的关系是“配合”与“协调”的关系。PowerBuilder完成数据请求、数据表现、菜单、界面等表象方面的工作,而数据库服务器完成数据库数据的存储管理、安全管理、并发控制、事务管理、完整性维护、查询优化等工作。
PowerBuilder在操作数据库时与以下几方面有关:
1、在数据库画笔中定义数据库表和视图
l 定义表的结构
l 表中列的扩展属性
² 定义表中列的显示风格
² 定义表中列的编辑屏蔽
² 定义表中列的校验
l 定义表的主键
l 定义表的外部键
l 定义表的索引
PowerBuilder有五个系统表,这五个系统表是PowerBuilder初次连接到数据库时系统自动建立的。下表给出了这个系统表的表名和它们的作用
PowerBuilder系统表 用途
PBCATTBL 存放表或视图,表或视图中列的缺省字体。
PBCATCOL 存放列用到的显示风格名、校验规则名和编辑风格名;列的标题、列的标签、字体的大小写,字体的对齐方式。
PBCATFMT 列的显示风格定义。
PBCATVLD 列的校验规则定义。
PBCATEDT 列的编辑风格定义。
表3-1 PowerBuilder系统表
2、在数据库画笔中在图形方式下操作数据库
这些操作包括:
l 插入记录
l 修改记录
l 删除记录
l 查询记录
l 把查询出的记录存入文件
l 把表或视图的定义转变成建表的SQL语名
这些功能是给开发人员和管理人员提供的。
3、在数据库画笔中用SQL语句执行平台管理操作数据库
l 生成数据库
l 管理数据库
l 维护数据
在这里创建的表和规则不会记录在PowerBuilder系统表中
4、在查询画笔中定义查询对象
如果一个查询动作要多次使用,还可以用PowerBuilder的查询画表生成查询对象这个查询对象不能由数据库画表调用,它只能由查询画表本身调用执行。在建立数据窗口时,数据源也可以做在查询对象之上。
事实上,查询对象就是写好了的SQL语句,它存在PowerBuilder的pbl文件中,在使用的时候调用即可。
5、用数据管道在不同数据库之间转换数据
数据管道允许把一个数据库的数据(一个或多个表中的全部或部分行)转入到
另一个数据库的一个表中(这个表可以存在或不存在),从而可以使数据在不同数据库之间相互复制。
6、用数据窗口操作数据库
Datawindow是PowerBuilder操作数据库的重要的手段,通过数据窗口可以查修改、插入和删除数据库的数据。PowerBuilder数据窗口操作数据库的能力非常强,是PowerBuilder的精华所在。
l 数据窗口的数据源可以是:
² 表或视图
² 多表连接
² 查询对象
² 外部数据源
² 存储过程
l 数据窗口列数据的显示风格
l 数据窗口列的编辑屏蔽
l 数据窗口列的校验
7、PowerScript直接调用SQL语句操作数据库
PowerBuilder对数据库的操作即可以通过数据窗口完成(本质上是数据窗口屏幕上对数据库的操作转化成SQL语句),又可以在PowerScript语言中直接调用SQL或存储过程(本质上仍是SQL语句)完成。详细操作在第*个章节已经介绍。
第四章 局域网图书资料查询系统设计分析
根据数据库系统生存期的设计方法,从数据库应用系统和开发的全过程来考虑,将数据库应用系统设计分为以下几个阶段(见图4-1)
1)规划;
2)需求分析;
3)概念设计;
4)逻辑设计
5)物理设计
§4.1 应用需求分析
要设计一个良好的局域网图书资料查询系统,就必须首先明确该应用环境对系统的要求。局域网图书资料查询系统的应用背景为:一个数十人的科研或开发小组,搜集了上千册有用的图书资料,其中有一部分分布在个人手中,为了方便大家查阅,需要对每本书的状态进行跟踪,另新进和丢失的图书资料必须得到及时的反映。因此,该系统需满足以下几方面需求:
l 用户的管理:必须具有使用权限的用户才能成功登录到系统中来。所谓用户权限在这里并不要求有功能上具体的划分,集中实行松散管理,这建立在用户高度自觉的基础上。所以,在这里只需给每个需要使用系统的人一个用户名和密码,即可登录系统进行各种操作。新的用户需要取得管理员的许可将其加入系统,加入系统的用户可以对自己的用户密码进行修改。
l 查询功能:系统需要提供几种不同方式的查询手段,以实现灵活方便地管理整个系统。
² 图书分类查询:一本图书包括书名、出版社、作者、保管人等多个信息,这就要求系统能按照不同的信息类别对图书进行查询。比如说,按书名查询、按作者查询等。选定需要的查询信息类别,再输入想查询的内容即可查询到相关的图书信息。
² 多条件查询:很多时候,用户需要了解的信息不仅仅局限于一个条件,比方说想同时查询书名为“数据库原理”但出版社仅为“经济科学出版社”的图书信息时,单纯的分类查询就不能满足用户的需要,这时就要用到多条件查询。也就是说,多条件查询不仅可以实现单个的分类查询,还可以实现多条分类查询的组合查询。每个查询条件之间用“并且”或“或者”的关系关联起来组成完整的查询条件。
² 模糊查询:对于用户来说并不一定完全记得某本图书准确的名称,类似,对于作者、出版社等等信息来说,很多时候用户只是记得一些相关的信息而不是一字不差的准确信息。这个时候就要用到模糊查询。用户输入查询内容后,系统将会把包含查询内容的(注意:并不是精确的等于查询内容)所有相关图书信息显示出来,以使用户得到准确的、自己真正需要的信息内容。
l 更新与编辑:
² 更新:系统允许用户对查询到的内容进行修改并且存盘。
² 编辑:系统允许用户对现库进行插入、删除的操作,保证现库的真实性与实时性。
l 打印输出:系统可以将用户查询到的内容动态地生成报表,并打印输出。
§4.2 系统功能模块划分
局域网图书图书资料查询系统功能划分模块图如下:
§4.3 系统数据库设计
4.3.1 概念设计
概念设计的目标是产生反映局域网图书资料查询系统需求的数据库概念结构,即概念模式。概念模式是独立于数据库逻辑结构,独立于支持数据库的DBMS,不依赖于计算机系统的。
l ER模型
ER模型是对现实世界的一种抽象。它的主要成分是实体、联系和属性。使
用这三种成分,我们可以建立许多应用环境的ER模型。
l ER模型的操作
在利用ER模型进行数据库概念设计的过程中,常常需要对ER图进行种种
变换。这些变换又称为ER模型的操作,包括实体类型、联系类型和属性的分裂、合并和增删等等。
l 利用ER方法的数据库概念设计
利用ER方法进行数据库的概念设计,可以分成三步进行:首先设计局部ER模式,然后把各局部ER模式综合成一个全局ER模式,最后对全局ER模式进行优化,得到最终的ER模式,即概念模式。
1. 设计局部的ER模式
通常,一个数据库系统都是为多个不同用户服务的。各个用户对数据的观点可能不一样,信息处理需求也可能不同。在设计数据库概念结构时,为了更好地模拟现实世界,一个有效的策略是“分而治之”,即先分别考虑各个用户的信息需求,形成局部概念结构,然后再综合成全局结构。在ER方法中,局部概念结构又称为局部ER模式,其图形表示称为ER图。
实体和属性的定义如下:
图书(资料编号,资料名称,作者,出版社,出版日期,ISBN,资料类别,购买日期,保管人,备注)
用户(编号,用户代码,用户姓名,登录口令,使用权限,查询显示项,用户显示头像)
资料类别(资料编号,资料类别)
出版社(出版社编号,出版社)
2. 联系定义:
ER模型的“联系”用于刻画实体之间的关联。一种完整的方式是对局部结构中任意两个实体类型,依据需求分析的结果,考察局部结构中任意两个实体类型之间是否存在联系。若有联系,进一步确定是1:N,M:N,还是1:1等。还要考察一个实体类型内部是否存在联系,两个实体类型之间是否存在联系,多个实体类型之间是否存在联系,等等。联系定义如图4-5所示。解释如下:
l 一个用户可以保管多本图书资料,而一本图书资料只能由一个用户保管;
l 一个资料类别可以对应多本图书,而一本图书只对应一个资料类别;
l 一本图书由一个出版社出版,而一个出版社可以出版多种图书。
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 登录模块图
该窗口所含的控件信息如下:
风格控件名说明
用户名 Single line edit Sle_1 ------
口令 Single line edit Sle_1 Propeties:passwd
登录日期 Edit Mask Em_1 mask type:datamm/dd/yyyy
表5-1 登录模块控件信息表
控件:Sle_1 事件(event):Modified Script of modified:
string input_code,namesetfocus() file://将光标定位在该控件input_code=string(this.text) file://接收用户输入的值,并赋值给变量select name into :name from keeperwhere id=:input_code or pym=:input_code;//从用户表里查找满足输入条件的记录if sqlca.sqlcode=0 then this.Text=name setfocus(sle_2) file://查找成功,则光标跳转到“口令”控件else if sqlca.sqlcode=100 then messagebox("提示!",& "对不起,用户不存在!",& StopSign!) return 1 file://查找不到符合条件的记录则提示 else messagebox("错误!",& "error:"+string(sqlca.sqldbcode)& +"information:"+sqlca.sqlerrtext) file://SQL出错提示 end ifend if
控件:Sle_2 事件(event):Modified Script of modified:
string input_passwd,passwd,droitinput_passwd=RightTrim(this.text)//去掉输入值右边的空格select passwd into :passwdfrom keeperwhere name=:sle_1.text;passwd=RightTrim(passwd);//从用户表中校验口令的正确性if sqlca.sqlcode=0 then if input_passwd <> passwd then messagebox('口令错误','对不起,请重新输入',stopsign!); sle_2.SelectText(1, Len(sle_2.Text)) this.Clear()//口令错误则清除输入内容 setfocus(sle_2) elseif input_passwd=passwd then user=righttrim(sle_1.text) select droit into :droit from keeper where name=:user;//口令正确将用户操作权限赋值给变量 open(main)//打开程序主窗口 close(w_login)//关闭登录窗口 end ifelseif sqlca.sqlcode=100 then messagebox('提示!','无数据',Stopsign!) return 1else messagebox("错误!",& "error:"+string(sqlca.sqldbcode)& +"information:"+sqlca.sqlerrtext) file://SQL出错提示end if
在登录模块中,用户可输入自己的编号或编码来登录系统。系统会根据用户输入的编号或编码值来自动给出确实已存在库中的用户中文名,若查找不到库中相匹配的记录则提示出错或重输。若用户存在,则提示输入口令,口令正确则会顺利进入该系统主界面。
§5.3 查询模块的实现
图5-3 查询窗口
该窗口(w_main)控件列表如下:
风 格 控件名 说 明
显示项 Group Box gb_1 ------
资料名称 Check Box cbx_1 Checked:true
作者 Check Box cbx_2 Checked:true
出版社 Check Box cbx_3 Checked:true
出版日期 Check Box cbx_4 Checked:false
类别 Check Box cbx_5 Checked:true
购买日期 Check Box cbx_6 Checked:false
ISBN Check Box cbx_7 Checked:false
保管人 Check Box cbx_8 Checked:true
备注 Check Box cbx_9 Checked:false
全选 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 将显示项存为当前用户默认
------ Single line edit sle_1 查询内容输入(字符型)
------ Edit mask em_1 查询内容输入(日期型)
------ Static text st_1 显示当前用户名
------ Picture P_1 修改按钮装饰
------ Picture P_2 Gb_1显示装饰
------ Picture P_3 显示当前用户头像
------ Drop down list box ddlb_1 查询项选择
------ Drop down list box ddlb_2 查询操作符选择
------ Tab control tab_1 共有三个tabpage頁tabpage1:模糊查询tabpage2:组合查询tabpage3:更新打印
------ Datawindows control dw_1 对应数据窗口d_information图书信息检索
------ Datawindows control dw_2 对应数据窗口d_query_condition查询条件检索
Command button Cb_2 模糊查询操作符英-中转换
表5-2 查询模块控件信息表
控件:w_main 事件(event):open Script of open
string mode,p,xs,headboolean xs_item[10]integer idw_1.settransobject(sqlca)dw_1.Retrieve()//打开窗口显示全部记录sj_check=0//为检索窗口排序变量赋初值st_1.text=user//在查询窗口右上角显示当前用户名SELECT "keeper"."display_item", "keeper"."head_picture" INTO :xs, :head FROM "keeper" WHERE "keeper"."name" = :user;//从用户表中读出当前用户的头像值及显示像值并分别赋值给变量 file://有头像的用户则显示在查询窗口右上角if isnull(head) thenp_3.visible=false else p_3.picturename=headend if ////将当前用户查询显示项变量读出并赋值给每个查询显示控件for i=1 to 8 if Mid (xs, i, 1)='0' then xs_item[i]=false else xs_item[i]=true end ifend for////给每个对应的CHECKBOX赋值,确实是勾还是叉cbx_2.checked = xs_item[1]cbx_3.checked = xs_item[2]cbx_4.checked = xs_item[3]cbx_5.checked = xs_item[4]cbx_6.checked = xs_item[5] cbx_7.checked = xs_item[6] cbx_8.checked = xs_item[7]cbx_9.checked = xs_item[8] ////对应的数据窗口显示情况dw_1.object.id.visible = falsedw_1.object.name.visible = truedw_1.object.author.visible = cbx_2.checkeddw_1.object.publishing_house.visible = cbx_3.checkeddw_1.object.publishing_date.visible = cbx_4.checkeddw_1.object.sort.visible = cbx_5.checkeddw_1.object.buy_date.visible = cbx_6.checkeddw_1.object.isbn.visible = cbx_7.checkeddw_1.object.keeper.visible = cbx_8.checkeddw_1.object.memo.visible = cbx_9.checked////将查询数据窗口设为只读dw_1.Object.DataWindow.ReadOnly="Yes"
控件:pb_12 “存为默认显示选项” 事件(event):clicked Script of clicked
boolean check[10]integer i,answerstring xs_item[10]string xs_content,question_text,tempquestion_text="将当前显示项存为"+user+"用户的默认显示项?"answer=messagebox('提示!',question_text,question!,YesNoCancel!)choose case answer case 1 file://将当前对应的CHECEKBOX选项记录下来check[1]=cbx_2.checked check[2]=cbx_3.checked check[3]=cbx_4.checked check[4]=cbx_5.checked check[5]=cbx_6.checked check[6]=cbx_7.checked check[7]=cbx_8.checked check[8]=cbx_9.checked// file://将显示项转化为01的显示,存入数组变量 xs_content='' for i = 1 to 8 if check[i]=true then xs_item[i]='1' else xs_item[i]='0' end if xs_content=xs_content+xs_item[i] end for// file://修改用户表中显示项的值 UPDATE "keeper" SET "display_item" = :xs_content WHERE "keeper"."name" = :user; choose case SQLCA.sqlcode case -1 messagebox("错误!","error:"+& string(sqlca.sqldbcode)+& "information:"+sqlca.sqlerrtext) messagebox('提示','修改成功!') commit; case 100 messagebox('!','修改失败!') end choose case 2 returncase 3 returnend choose
5.3.1 模糊(分类)查询的实现
图5-4 模糊查询窗口
控件:ddlb_1 事件(selectionchanged): Script of Selectionchanged
file://清空历史查询内容em_1.selecttext(1,len(em_1.text))em_1.clear()sle_1.selecttext(1,len(sle_1.text))sle_1.clear()//运算符随查询内容的改变而改变if ddlb_1.text= '出版日期' or ddlb_1.text='购买日期' then ddlb_2.deleteitem(ddlb_2.FindItem("包含", 2)) ddlb_2.deleteitem(ddlb_2.FindItem("不包含", 2)) ddlb_2.deleteitem(ddlb_2.FindItem("小于等于", 2)) ddlb_2.deleteitem(ddlb_2.FindItem("大于等于", 2))ddlb_2.insertitem('小于',3)ddlb_2.insertitem('大于',4)ddlb_2.insertitem('小于等于',5) ddlb_2.insertitem('大于等于',6) em_1.TextColor = RGB(255,0,0) sle_1.visible = False em_1.SetMask(2005-7-23 12:11:45Mask!, 'mm/dd/yyyy')else ddlb_2.deleteitem(ddlb_2.FindItem("小于", 2)) ddlb_2.deleteitem(ddlb_2.FindItem("大于", 2)) ddlb_2.deleteitem(ddlb_2.FindItem("小于等于", 2)) ddlb_2.deleteitem(ddlb_2.FindItem("大于等于", 2)) ddlb_2.deleteitem(ddlb_2.FindItem("包含", 2)) ddlb_2.deleteitem(ddlb_2.FindItem("不包含", 2)) ddlb_2.insertitem('包含',3) ddlb_2.insertitem('不包含',4)sle_1.visible= Truesle_1.TextColor = RGB(255,0,0)end if
不可见控件:cb_2 “转换” 事件(event):Clicked Script of Clicked
file://将查询项转换为表info中字段名choose case ddlb_1.text case '资料名称' query_item='name' case '作者' query_item='author' case '出版社' query_item='publishing_house' case '出版日期' query_item='publishing_date' case 'ISBN' query_item='ISBN' case '资料类别' query_item='sort' case '购买日期' query_item='buy_date' case '保管人' query_item='keeper' case '备注' query_item='memo'end choose//将操作符转换为中文显示choose case ddlb_2.text case '等于' query_operator='=' case '包含' query_operator='like' case '不等于' query_operator='<>' case '不包含' query_operator='not like' case '小于等于' query_operator='<=' case '大于等于' query_operator='>=' case '小于' query_operator='<' case '大于' query_operator='>'end choose
控件:pb_1 “检索” 事件(event):Clicked Script of Clicked
string rqdate dcb_2.TriggerEvent(clicked!)//将英文操作符转换为中文显示dw_1.setredraw(true)old_sql = dw_1.getsqlselect()//得到原有数据窗口的SQL语句if ddlb_1.text='出版日期' or ddlb_1.text='购买日期' then//查询项为日期类型 em_1.GetData(d)//取出输入的日期数据 rq = String(d, "yyyy/mm/dd")//将输入的日期数据转换为字符型 choose case query_operator//根据操作符的不同将新产生的WHERE条件与老的SQL语句拼接成新的SQL语句 case '=' new_sql = old_sql + " where " + query_item & + " "+query_operator + 'date' +'(' + "'" + rq + "'" +')' case '<>' new_sql = old_sql + " where " + query_item & + " "+query_operator + 'date' +'(' + "'" + rq + "'" +')' & +' or '+query_item+' is null ' case '<=' new_sql = old_sql + " where " + query_item & + " "+query_operator + 'date' +'(' + "'" + rq + "'" +')' case '>=' new_sql = old_sql + " where " + query_item & + " "+query_operator + 'date' +'(' + "'" + rq + "'" +')' end choose////查询项为字符型else sle_1.text=Righttrim(sle_1.text)//将当前输入内容右面的空格去掉 choose case query_operator//根据操作符的不同将新产生的WHERE条件与老的SQL语句拼接成新的SQL语句 case '=' new_sql = old_sql + " where " + query_item & + " "+query_operator + "'" + parent.sle_1.text + "'" case '<>' new_sql = old_sql + " where " + query_item & + " "+query_operator + "'" + parent.sle_1.text + "'"& +' or '+query_item+' is null ' case 'like' new_sql = old_sql + " where " + query_item & + " "+query_operator + "'" +'%'+ parent.sle_1.text +'%'+ "'" case 'not like' new_sql = old_sql + " where " + query_item & + " "+query_operator + "'" +'%'+ parent.sle_1.text +'%'+ "'"& + ' or '+query_item+' is null 'end choose//end if//对原有数据窗口进行新的查询 if dw_1.setsqlselect(new_sql) = -1 then beep(3) messagebox("警告", "检索失败",stopsign!) elsedw_1.settransobject(sqlca) dw_1.retrieve() dw_1.setsqlselect(old_sql) end if
5.3.2 组合查询(条件查询)模块的实现:
图5-5 组合(条件)查询窗口
控件:dw_2 事件(event):Itemchanged Script of Itemchanged
string item,status,edit,result,modify_string,cur_string,c,operator1this.accepttext()choose case dwo.name case'column_name' item=dw_2.getitemstring(dw_2.getrow(),'column_name')//根据查询项目改变操作符的显示If item='publishing_date' or item='buy_date' then dw_2.setrow(dw_2.getrow()) dw_2.setitem(row,'content','') dw_2.setvalue('operator',1,'等于~t=') dw_2.setvalue('operator',2,'不等于~t<>') dw_2.setvalue('operator',3,'小于~t<') dw_2.setvalue('operator',4,'大于~t>') dw_2.SetValue('operator', 5, '小于等于~t<=') dw_2.SetValue('operator', 6, '大于等于~t>=') Status = dw_2.GetValue('operator',4)else dw_2.setrow(dw_2.getrow()) dw_2.setitem(row,'content','') dw_2.setvalue('operator',1,'等于~t=') dw_2.setvalue('operator',2,'不等于~t<>') dw_2.SetValue('operator', 3, '包含~tlike') dw_2.SetValue('operator', 4, '不包含~tnot') Status = dw_2.GetValue('operator',4)end if//case 'operator'//防止操作符的显示改变 operator1=dw_2.getitemstring(dw_2.getrow(),'operator') choose case operator1 case 'like' dw_2.setitem(dw_2.getrow(),'operator','包含') case 'not' dw_2.setitem(dw_2.getrow(),'operator','不包含')end chooseend choose
控件:pb_2 “插入” 事件(event):Clicked Script of Clicked
int row,new_row,istring queryrow=dw_2.getrow()//将当前行的行号赋值给变量//允许插入一行的条件 if (isnull(dw_2.getitemstring(row,'column_name')) or & isnull(dw_2.getitemstring(row,'operator')) or & isnull(dw_2.getitemstring(row,'content')) or & isnull(dw_2.getitemstring(row,'join'))) then query='no' else query='yes' end if//如果允许插入,则在当前行后插入一条新记录if query='yes' thennew_row=dw_2.insertrow(0)////将光标定位在新的一行“资料名称”列dw_2.scrolltorow(new_row)dw_2.setrow(new_row)dw_2.setcolumn(1)//else messagebox('警告',"请输入完整的查询条件",stopsign!)end if
控件:pb_3“删除” 事件(event):Clicked Script of Clicked
int row//如果当前行不为第一行,则允许删除if dw_2.getrow()<>1 then row=dw_2.getrow()//将当前行行号赋值给变量 dw_2.deleterow(row)//删除当前行else returnend if
控件:pb_4“检索” 事件(event):Clicked Script of Clicked
Long row_count,rowstring new_condition,itemstring condition[100],column_name,operator,content,join,operator1int i//将当前行行号赋值给变量row=dw_2.getrow()//将当前行列名赋值给变量column_name=dw_2.getitemstring(row,'column_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 (isnull(column_name) or & isnull(operator) or & isnull(content)) then dw_2.deleterow(row) end if end if ////将查询条件窗口的总行数赋值给变量row_count=dw_2.rowcount()//where后的表达式for i=1 to row_count item=dw_2.getitemstring(i,'column_name') operator1=dw_2.getitemstring(i,'operator') choose case item//查询项为字符型 case 'name','author','publishing_house','sort','isbn','keeper','memo' if i<> row_count then//查询条件有多行时 choose case operator1 case 'not'//操作符为不包含 condition[i]=dw_2.getitemstring(i,'column_name')+" "+& dw_2.getitemstring(i,'operator')+" "+"like"+& "'"+'%'+dw_2.getitemstring(i,'content')+'%'+"'"+dw_2.getitemstring(i,'join')+" " case 'like'//操作符为包含 condition[i]=dw_2.getitemstring(i,'column_name')+" "+& dw_2.getitemstring(i,'operator')+" "+& "'"+'%'+dw_2.getitemstring(i,'content')+'%'+"'"+dw_2.getitemstring(i,'join')+" " case '=','<>'//操作符为等于或不等于 condition[i]=dw_2.getitemstring(i,'column_name')+" "+& dw_2.getitemstring(i,'operator')+" "+& "'"+dw_2.getitemstring(i,'content')+"'"+dw_2.getitemstring(i,'join')+" " end choose else//查询条件只有一行 choose case operator1 case 'not'//操作符为不包含 condition[i]=dw_2.getitemstring(i,'column_name')+" "+& dw_2.getitemstring(i,'operator')+" "+"like"+& "'"+'%'+dw_2.getitemstring(i,'content')+'%'+"'" case 'like'//操作符为包含 condition[i]=dw_2.getitemstring(i,'column_name')+" "+& dw_2.getitemstring(i,'operator')+" "+& "'"+'%'+dw_2.getitemstring(i,'content')+'%'+"'" case '=','<>'//操作符为等于或不等于 condition[i]=dw_2.getitemstring(i,'column_name')+" "+& dw_2.getitemstring(i,'operator')+" "+& "'"+dw_2.getitemstring(i,'content')+"'" end choose end if//查询项为日期类型case 'publishing_date','buy_date' if i<> row_count then//查询条件有多行时 condition[i]=item+" "+dw_2.getitemstring(i,'operator')+" "+& "date('"+dw_2.getitemstring(i,'content')+"')"+dw_2.getitemstring(i,'join')+" " else//查询条件只有一行 condition[i]=item+" "+dw_2.getitemstring(i,'operator')+" "+& "date('"+dw_2.getitemstring(i,'content')+"')" end ifend choosenext//最终的where后的表达式new_condition=''for i= 1 to row_count new_condition=new_condition+condition[i]nextold_sql1=dw_1.getsqlselect()new_sql1=old_sql1+ " where " + new_condition//对原有数据窗口进行新的查询 dw_1.reset()if dw_1.setsqlselect(new_sql1) = -1 then beep(3) messagebox("警告", "检索失败",stopsign!) else dw_1.settransobject(sqlca) dw_1.retrieve() dw_1.setsqlselect(old_sql1) end if
5.3.3 更新打印模块实现
图5-6 更新打印窗口
控件:pb_7“修改” 事件(event):Clicked Script of Clicked
file://取消亮条显示一行,便于进行编辑dw_1.selectrow(dw_1.getrow(),false)//将数据窗口属性设为可以修改dw_1.Object.DataWindow.ReadOnly="no"dw_1.settransobject(sqlca)//将焦点设置在数据窗口控件上dw_1.setfocus()//触发dw_1的Getfocus事件dw_1.TriggerEvent(Getfocus!)//将插入、删除、存盘、打印按钮全部设为可以使用pb_8.enabled = truepb_9.enabled = truepb_10.enabled = truepb_11.enabled = true//
控件:pb_8“插入” 事件(event):Clicked Script of Clicked
string statuslong row//在当前行后插入一行dw_1.ScrollToRow (dw_1.insertrow(dw_1.getrow()))//设置焦点在数据窗口dw_1.setfocus()//触发数据窗口事件dw_1.TriggerEvent(Getfocus!)
控件:pb_9“删除” 事件(event):Clicked Script of Clicked
int answer//将当前行亮条显示dw_1.selectrow(dw_1.getrow(),true)answer=messagebox("提示!","确实删除此记录?",Question!,YesNoCancel!)choose case answer case 1 file://删除当前记录 dw_1.deleterow(dw_1.getrow()) file://触发dw_1的getfocus事件 dw_1.triggerevent(getfocus!) case 2,3 file://取消当前行亮条显示 dw_1.selectrow(dw_1.getrow(),false) file://将光标定位在dw_1 dw_1.setfocus() file://触发dw_1的getfocus事件 dw_1.TriggerEvent(Getfocus!)end choose
控件:pb_9“删除” 事件(event):Clicked Script of Clicked
int answerlong del_count// 将数据窗口删除记录标记值赋值给变量del_count=dw_1.deletedcount()//判断数据窗口是否有记录标记为删除或者否有列被修改if (modifiedcount(dw_1)<>0) or (del_count<>0) then//若数据窗口中记录被改动询问是否存盘 answer=messagebox("提示!","是否存盘?",Question!,YesNoCancel!) choose case answer case 1if dw_1.update()=1 then//如果数据窗口具有UPDATE属性 messagebox('提示','存盘成功!') commit; end if case 2//数据窗口不具有UPDATE属性则回滚 rollback; dw_1.ResetUpdate ( ) dw_1.reset()//清空数据窗口 dw_1.retrieve()//刷新数据窗口 dw_1.setfocus()//设置焦点在dw_1 dw_1.TriggerEvent(Getfocus!)//触发dw_1的getfocus事件 case 3 dw_1.setfocus() dw_1.TriggerEvent(Getfocus!) end chooseend if
控件:dw_1 事件(event):Getfocus Script of Getfocus
this.setrow(this.getrow())this.setcolumn('name')//设置当前列为“资料名称”this.accepttext()
控件:dw_1 事件(event):Lostfocus Script of Lostfocus
file://在编辑状态,接收输入的字符this.accepttext()
控件:dw_1 事件(event): Clicked Script of Clicked
string cur_row//将当前行赋值给变量cur_row=string(dw_1.getclickedrow())//如果数据窗口为只读、或至少有一条记录则亮条显示当前焦点所在记录if dw_1.object.datawindow.readonly='yes' and cur_row<>'0' then dw_1.selectrow(getrow(),true)end if//
控件:dw_1 事件(event):Rowfocuschanged Script of rowfocuschanged
string cur_row//将鼠标单击选中的行号赋值给变量cur_row=string(dw_1.getclickedrow())//数据窗口只读或当前行号不为零,则亮条显示选中记录if dw_1.object.datawindow.readonly='yes' and cur_row<>'0' then dw_1.selectrow(dw_1.getrow(),true)end if//
控件:dw_1 事件(event):Rowfocuschanging Script of rowfocuschanging
file://焦点改变时将旧焦点的亮条显示取消dw_1.selectrow(dw_1.getrow(),false)
控件:dw_1 事件(event):DoubleClicked Script of DoubleClicked
integer cur_row,sjstring new_sort,cur_column,cur_textif sj_check=0 then sj=0//表示按升序排序else sj=1//表示按降序排序end if//取消当前行亮条显示dw_1.selectrow(dw_1.getrow(),false)if sj=0 then cur_text=dwo.name//将当前双击对象名赋值给变量//表示只有双击列标题才实现排序cur_column=left(cur_text,len(cur_text)-2)//去掉列标题的_t//按升序排序 new_sort=string(cur_column)+' A' dw_1.setsort(new_sort) dw_1.sort()//sj_check=1//再次双击变成降序排序else cur_text=dwo.name//将当前双击对象名赋值给变量//表示只有双击列标题才实现排序 cur_column=left(cur_text,len(cur_text)-2) file://去掉列标题的_t//按降序排序new_sort=string(cur_column)+' D' dw_1.setsort(new_sort)dw_1.sort()//sj_check=0//再次双击变成升序排序end if
§5.4 数据维护模块的实现
数据维护模块包括三个子模块,它们分别是:用户管理模块、资料类别管理模块
出版社管理模块。它们的窗口及控件定义如下:
窗口名 控件 风格 控件名 说明
用户管理模块w_keeper_dic 修改 Picture box Pb_1
插入 Picture box Pb_2
删除 Picture box Pb_3
存盘 Picture box Pb_4
加入头像 Picture box Pb_5
退出 Picture box Pb_6
Drop down picture list box Ddplb_1 下拉图片框
D_keeper_dic Datawindow control Dw_1
资料类别管理模块w_sort_dic 修改 Picture box Pb_1
插入 Pictue box Pb_2
删除 Picture box Pb_3
存盘 Picture box Pb_4
退出 Picture box Pb_5
D_sort_dic Datawindow control Dw_1
出版社管理窗口w_publishinghouse_dic 修改 Picture box Pb_1
插入 Picture box Pb_2
删除 Picture box Pb_3
存盘 Picture box Pb_4
退出 Picture obx Pb_5
D_publishing_dic Datawindow control Dw_1
表5-3 数据维护模块控件信息表
5.4.1 用户管理模块
图5-7 用户管理窗口
控件:dw_1 事件(event):Getfocus Script of Getfocus
this.setrow(this.getrow())this.setcolumn('id') file://设置当前列为“编号”this.accepttext()
控件:pb_1 “修改” 事件(event):Clicked Script of Clicked
dw_1.selectrow(dw_1.getrow(),false)//取消当前行亮条显示dw_1.Object.DataWindow.ReadOnly="no"//设当前数据窗口可以编辑dw_1.settransobject(sqlca)dw_1.setfocus()//将焦点置在数据窗口dw_1dw_1.TriggerEvent(Getfocus!)//触发dw_1的getfocus事件pb_2.enabled = true//插入按钮可以使用pb_3.enabled = true//删除按钮可以使用pb_4.enabled = true//存盘按钮可以使用ddplb_1.enabled = true//加入头像按钮可以使用
控件:pb_2 “插入” 事件(event):Clicked Script of Clicked
string statuslong row dw_1.ScrollToRow (dw_1.insertrow(dw_1.getrow()))//将光标置在新插入的行上dw_1.setfocus()dw_1.TriggerEvent(Getfocus!)
控件:pb_3 “删除” 事件(event):Clicked Script of Clicked
int answerdw_1.selectrow(dw_1.getrow(),true)//亮条显示将要删除的记录answer=messagebox("提示!","确实删除此记录?",Question!,YesNoCancel!)choose case answer case 1 dw_1.deleterow(dw_1.getrow())//删除记录 dw_1.triggerevent(getfocus!) case 2,3 dw_1.selectrow(dw_1.getrow(),false) dw_1.setfocus() dw_1.TriggerEvent(Getfocus!)end choose
控件:pb_4 “存盘” 事件(event):Clicked Script of Clicked
int answerlong del_countdel_count=dw_1.deletedcount()//将标记为删除的记录数量赋值给变量if (modifiedcount(dw_1)<>0) or (del_count<>0) then//数据窗口是否修改 answer=messagebox("提示!","是否存盘?",Question!,YesNoCancel!) choose case answer case 1 if dw_1.update()=1 then messagebox('提示','存盘成功!') commit;//存盘成功提交 end if case 2 rollback;//存盘不成功回滚 dw_1.ResetUpdate ( ) dw_1.reset()/ dw_1.retrieve() dw_1.setfocus() dw_1.TriggerEvent(Getfocus!) case 3 dw_1.setfocus() dw_1.TriggerEvent(Getfocus!) end chooseend if
控件:pb_5 “加入头像” 事件(event):Clicked Script of Clicked
integer indexstring pic_namelong rowrow=dw_1.getrow()//当前行赋值给变量index=integer(ddplb_1.text)//将下拉图片框的图片编号赋值给变量pic_name=string(ddplb_1.picturename[index])//将图片存储路径及名称赋值给变量dw_1.setitem(row,'head_picture',pic_name)//给数据窗口头像栏赋值dw_1.setfocus()//dw_1.triggerevent(getfocus!)dw_1.setrow(dw_1.getrow())dw_1.setcolumn('head_picture')//光标设置在头像字段
控件:ddplb_1 事件(event):Selectionchanged Script of Selectionchanged
file://头像选项改变时保证焦点的位置(在当前行的头像字段)dw_1.setrow(dw_1.getrow())dw_1.setcolumn('head_picture')
控件:pb_6 “退出” 事件(event):Clicked Script of Clicked
int answerlong del_countdel_count=dw_1.deletedcount()//将标记为删除的记录数量赋值给变量if (modifiedcount(dw_1)<>0) or (del_count<>0) then//数据窗口是否修改 answer=messagebox("留步!","有未存盘数据,是否退出?",Question!,YesNo!) choose case answer case 2 dw_1.setfocus() dw_1.TriggerEvent(Getfocus!) case 1 rollback;//放弃存盘回滚 close(w_keeper_dic)end chooseelse close(w_keeper_dic)end if
5.4.2 资料类别管理模块
图5-8 资料类别管理窗口
5.4.3 出版社管理模块
图5-9 出版社管理窗口
u 注解:鉴于“资料类别管理模块”与“出版社管理模块”的功能与“用户管理模块”的功能相似,且代码有重复,所以,在这里不再对这两个模块的代码进行详细列举和分析。
第六章 结束语
一个应用程序设计开发的好坏,与设计人员对开发工具的掌握程度息息相关。
在本系统的开发设计过程中,由于本人对开发工具的掌握有限,又是完全独立完成,可以说整个的开发过程是一边摸索一边实践出来的。但令人高兴的是,通过这样一个边学习边应用的过程,本人完成了局域网图书资料查询系统的开发工作,并实现了该应用程序背景所要求的功能。本人考虑到查询系统的特点,在开发工具PB并不容易实现优秀界面的情况下,着实在界面的设计方面花费了一番功夫,并借助了工具软件PowerPoint来实现。
但总的来说,程序仍然存在许多不足之处,下面列举如下:
1)多条件(组合)查询中,对于查询内容为日期类型的值的情况,没有掩码控制机制(在模糊(分类)查询中能够实现)。组合条件是用一个数据窗口实现的,由于输入的值既有字符型又有日期型,而动态改变数据窗口的函数Modified( )不能改变数据窗口列的属性,所以一时无法找到好的解决办法。而在单条件(模糊、分类)查询中是用一个控件(例如:Singled line edit、Edit mask)来接收查询内容的输入,相对容易实现。
2)大量数据的录入问题:在设计过程中,本人曾考虑到再做一个数据录入的子菜单项,主要是把界面做得尽可能大一些,没有现在的查询窗口中那么多子项,但由于时间等各方面原因没有做成。虽然程序能够实现录入的功能,但从方便用户的角度出发,大量数据的录入界面是有必要的。
3)模糊查询的扩充:在本系统中,尽管实现了分类的模糊查询,例如,对资料名称的模糊查询,但是缺少对全部字段的模糊查询功能。其实在实现了分类的模糊查询之后,全部字段的模糊查询功能实现起来原理相似,遗憾的是,时间仓促,仍然没有完成这部分代码。
4)程序中仍然存在校验不严格的问题:在编辑更新这个模块中,对某些字段的输入校验仍然不严格,有待进一步改进。
总的来说,虽然该系统还有很多不足之处有待日后改进,但是在整个开发过程中本人一直本着认真、虚心、刻苦、积极的态度,在有课业压力的情况下仍然坚持自己独立完成设计,并基本达到了应用设计的功能要求。希望该系统在日后能够得到不断改进,发挥出它应有的作用。
致谢:在本系统的开发设计过程中,本人得到了父母、老师、朋友、以及同事们的大力支持与帮助。感谢老师在具体设计过程中给予的悉心指导;感谢同事们在环境上与时间上的支持;特别是感谢父母在生活上和精神上给予的支持,这是我能够坚持下来的源动力。
再次向所有在本人设计过程中给予过帮助与关怀的所有朋友表示深深的感谢!
参考书目:
[1] 经济科学出版社.丁宝康.《数据库原理》.1999.5
[2] 晓通网络数据库研究所.《PowerBuilder开发与参考手册》.内蒙古人民出版社 1998.10
[3] 晓通网络数据库研究所.《PowerScript程序设计指南》.内蒙古人民出版社
1998.10
[4] 晓通网络数据库研究所.《PowerBuilder开发中的数据库设计》.内蒙古人民出版社
1998.10