局域网图书资料查询系统
的标准-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列名[ASCDESC]…]
句法中[]表示该成分可有,也可无。
整个语句的执行过程如下:
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(DateMask
!, '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(