- 论坛徽章:
- 0
|
INFORMIX报表工具的使用及其C语言接口
庄 文 祥
(中国工商银行惠安县支行,惠安 362100)
摘要:本文介绍如何使用INFORMIX提供的报表工具编制报表的方法,同
时利用其提供的C语言接口解决一些用报表工具难于解决的问题。
关键词:INFORMIX 报表工具 C语言接口
1 INFORMIX报表工具简介
INFORMIX报表工具是ISQL软件包中的一种,简称ace。通常利用vi编写报表的格式文件,
其文件后缀是ace,用saceprep编译成arc格式再用sacego执行。利用该工具我们可以比较
简便地编写业务报表而不用编写相对复杂的C语言程序。下面用一个例子来说明具体的使
用方法,该例具有通用性,稍加修改就可编写其他报表。
2 报表格式文件的书写
格式文件一般分几个节,下面以fxt.ace为例逐一介绍。注释行用“{ }”。
2.1 数据库节
该节用来指定要提取数据的数据库名称是icbdb。
Database icbdb end
也可在运行时用“-d”参数重新指定目的数据库,如:sacego -d icbdb2 fxt。
2.2 Define节
定义变量、命令行参数以及要引用的C语言接口函数等。
define
variable fxtx char(3)
variable i integer
variable xc5 money(16,2)
param[1] num_arg char(3) {第一个命令行参数}
function validate
end
命令行参数的使用方法是:sacego -q -s fxt 114,sacego要带“-s”参数运行,参数
“114”将会赋值给变量num_arg。
2.3 输入输出节
输入节用来接收用户输入的参数,输出节定义报表页面的上下左右空白和生成报表的
文件名。用户输入的交换号存放在变量fxtx中,报表文件名为“dkqx.txt”。
input
prompt for fxtx using "输入交换号:"
end
output
left margin 0
report to "dkqx.txt"
end
2.4 Select节
指定报表数据来源的SQL语句,我们的例子是要生成每个存款帐号所对应的表内外欠息,
数据,要从欠息明细文件中按存款帐号分组统计出来并且按存款科目分类。其中几张表结
构如下,它们的关系是一个存款帐户对应一条或多条欠息明细记录,也可能没有欠息。由
于数据存在不完整的现象,fxt表中的存款帐号不一定会在fxb表中,所以统计时将fxb作为
外表(outer),建立fxb和fxt间的外连接以保证fxt中的记录都能被输出。
由于fxt表中表内外欠息数据不是两个域而是通过fxt040来区别,这给统计带来了不便,
所以使用了临时表t1和t2,t1表先按存款帐号分组统计每个帐号的表内外欠息总和,t2表
再按存款帐号分组统计每个帐号的表外欠息,最后才通过存款帐号将这两个临时表连起来
一起生成报表,由于存在有表内欠息而无表外欠息的情况,所以也要将t2表作为外表,以
保证统计出来的所有欠息记录都被输出。使用临时表才能将表内外欠息数据并排打印在一
行上,临时表在报表输出后即被删除。
select distinct fxt070 c1,fxt080 c2,fxt090 c3,fxb050 c4,sum(fxt120) c5,
fxb040 c6 from fxt,outer fxb where fxb010=fxt070 and fxb020=fxt080
and fxb030=fxt090 and fxt070=$fxtx group by fxt070,fxt080,fxt090,
fxb050,fxb040 having sum(fxt120)!=0 into temp t1;
select distinct fxt070 c1,fxt080 c2,fxt090 c3,fxb050 c4,sum(fxt120) c5,
fxb040 c6 from fxt,outer fxb where fxb010=fxt070 and fxb020=fxt080
and fxb030=fxt090 and fxt040="1" and fxt070=$fxtx group by fxt070,
fxt080,fxt090,fxb050,fxb040 having sum(fxt120)!=0 into temp t2;
select distinct t1.c1,t1.c2 kmh,t1.c3,t1.c4,t1.c5 cc5,
t2.c5,t1.c6,fxi.fxi020 from t1,outer t2,fxi
where t1.c1=t2.c1 and t1.c2=t2.c2 and t1.c3=t2.c3 and
t1.c1=$fxtx and t1.c1=fxi.fxi010 order by kmh end
①.存款分户帐,表名:fxb
fxb010 char(3) 帐户交换号
fxb020 char(4) 帐户科目号
fxb030 char(4) 帐户顺序号
fxb040 char(3) 帐户校验码
fxb050 char(30) 存款户名
②.企业欠息明细帐,表名:fxt
fxt010 char(3) 交换号
fxt020 char(4) 贷款科目
fxt030 char(4) 贷款借据号
fxt040 char(1) '0':表内,'1':表外
fxt070 char(3) 存款交换号
fxt080 char(4) 存款科目号
fxt090 char(4) 存款顺序号
fxt120 money(16,2) 欠息数据
③.交换号,表名:fxi
fxi010 char(3) 交换号
fxi020 char(20) 交换行名
2.5 Format节
在这里定义报表的详细格式,也是比较麻烦的一节。由于数据还要求按科目分组,所
以要处理好页头与组头,页尾与组尾间的关系,以避免出现双表头双表尾的现象。该节以
“Format”开头以“End”结束,下面的7个小节放在中间。
2.5.1 第一页
因为会计数据已集中在市分行,所以在第一页开始之前要根据用户输入的交换号,判
断该交换号是否在县支行的管辖范围之内,以免打印出别的支行的数据。比如惠安支行下
辖一个业务部和三个分理处,一共有四个交换号,那么我们就只能生成这四个交换行处的
报表,此时就用到C语言编写的函数validate和abort。在这里还可作一些变量的初始化。
first page header
if validate(fxtx) then begin call abort() end
let ag=2
let pt=0
2.5.2 页头
在每一页打印纸开头用print语句打印报表的表头,可以用column控制字符串的起始位
置。由于报表工具提供的日期语句(date)返回的是英文格式,所以我们又使用C语言编写了
一个函数get_date返回中文格式的日期。同时设页头标志ph为1,页尾标志pt为0。
page header
let ph=1
let pt=0
print column 10,"中国工商银行",fxi020 clipped,
kmh,"科目企业欠息统计表","(",get_date()," "
for i=1 to 80 do print "=";
print
print column 1,"序号",column 10,"存款帐号",
column 22,"存款户户名",column 56,"表内欠息",column 71,"表外欠息"
for i=1 to 80 do print "=";
print
2.5.3 分组之前
在每个科目分组之前要判断如果页头已打印表头则不重复打印表头,还要判断如果一
页剩下的行数不足于打印表头、至少一行数据以及表尾就要跳到新的一页,此时就要用到
内部变量lineno(当前行号)。同时设分组后标志为0。
before group of kmh
let nc11=nc1 {nc1清0前先保存起来}
let nc1=0
let nc2=0
let nc3=0
if lineno>;53 then begin
skip to top of page {跳页}
end
let ag=0
if ph=0 and lineno!=8 then begin
{打印表头}
end
2.5.4 分组之后
每个科目分组后要打印表尾,表尾打印出页号、户数及合计数。首先要判断如果当前
行号已是57就不打印表尾,因为此时报表就要分页了,表尾就由页尾节去处理。如果页尾
已经出现(pt=1,此时表尾已经打印了)后恰好又遇到分组结束,此时也不打印表尾。同时设
页头标志为0,分组后标志为1。
after group of kmh
let ph=0
let ag=1
let xc4=group total of cc5
let xc5=group total of c5 {分组统计}
if lineno>;=57 or pt=1 then begin
let ag=0
end else begin {不是页尾则打印表尾}
for i=1 to 80 do print "=";
print
print "页号:",pageno using "##"," ",
"户数:",group count using "####"," ",
"合计:",column 51,xc4-xc5 using "###,###,###.##",
column 66,xc5 using "###,###,###.##"
if lineno>;9 then begin
skip 2 lines {每组间隔两行}
end
end
2.5.5 打印数据行
此处具体打印数据。如果有表内而无表外欠息,此时表外欠息为空值,所以只有在表
外欠息c5非空值时,才能用xc4=cc5-c5语句计算表内欠息时(cc5为单个存款户表内外欠息
的总和),否则计算出来的xc4也会是空值,就无法计算出表内欠息的数据。当表外欠息为
空值时xc4即为表内欠息,同时要将表外欠息数据置0。
on every row
let nc1=nc1+1 {序号加一,同时计算分组内的户数}
let xc4=cc5 {表内外欠息之和}
let xc5=c5 {表外欠息}
if c5 is not null then begin {判断是否空值}
let xc4=cc5-c5 {表内欠息}
let nc3=nc3+c5 {计算分组的表外欠息小计}
end else begin
let xc5=0 {表外欠息为空值则置零}
end
let nc2=nc2+xc4 {计算分组的表内欠息小计}
let hm=c4 {户名,取前14个汉字}
print column 1,nc1 using "####", {序号}
column 6,c1, {交换号}
column 10,kmh,column 14,c3,
column 18,c6, {以上三项打印存款帐号及校验码}
column 22,hm, {存款户名}
column 51,xc4 using "###,###,###.##", {表内欠息}
column 66,xc5 using "###,###,###.##" {表外欠息}
2.5.6 页尾
在每一张报表的页尾也要打印表尾。如果分组还未结束就要打印表尾,此时的合计数
为本组内当前的合计数而不是全组的合计数。如果分组结束时正好分页,after group节将
不打印表尾,而是将标志ag置0后由本节来打印表尾,因为分组结束后必然又先开始新的分
组然后才来执行页尾的处理,而此时变量nc1、nc2、nc3已经清零(见before group节),所
以要重新计算。
报表工具的if..else语句要求在if子句中打印几行在else子句也要打印出同样的行数,
以保证格式的完整性,否则编译出错。页尾设计共占6行,由于使用缺省的上下边界(各占3
行),加上表头4行和表尾的6行,一共是16行,一页打印纸能打印66行,这样在打满一页时
正好有50行数据。另外由于页尾占用6行加上留白的3行,所以打印到57行时就会分页。
page trailer
let pt=1 {页尾标志置1}
if ag=0 then begin {未遇到分组结束或分组结束时恰好分页}
let ncx=nc1
if nc1=0 then begin {页尾时正好分组结束并开始新的分组}
let ncx=nc11 {在nc1清0前已将nc1的值保存在nc11中}
let nc2=xc4-xc5 {xc4,xc5已在after group节中算出}
let nc3=xc5
end
for i=1 to 80 do print "=";
print
print "页号:",pageno using "##"," ","户数:",ncx using "####"," ",
"合计:",column 51,nc2 using "###,###,###.##",
column 66,nc3 using "###,###,###.##"
end else begin {上面共打印两行}
skip 2 lines {保证报表格式的完整性}
end
skip 4 lines
2.5.7 最后一行
整份报表可在此输出总的统计数据。因为生成的报表文件名都一样,不能根据输入的
交换号改变报表的文件名,所以要根据交换号的不同将报表放在不同的目录中。报表工具
是没有此项功能的,为此我们又用到C语言函数mv_file,比如mv_file(fxtx,"dkqx.txt"
将文件dkqx.txt移到/usr/icbacct/tmp/$fxtx目录中。
on last row
let xc4=total of cc5 {表内外欠息总值}
let xc5=total of c5 {表外欠息总值}
print "户数:",count using "#####"," ", {count算出总户数}
"表内:",xc4-xc5 using "#,###,###,###.##"," ",
"表外:",xc5 using "#,###,###,###.##"," ",
"总计:",xc4 using "#,###,###,###.##"
call mv_file(fxtx,"dkqx.txt"
3 C语言接口
从上面可以看出报表工具提供的C语言接口可以解决工具难于解决的一些问题。C语言
程序要按照一定的格式编写并与某些函数库连接,再编译成工具软件新的版本。saceprep
仍可用来编译格式文件,但运行时不能再用sacego了,此时要用生成的新版本newacego。
3.1 C语言程序
3.1.1 初始定义
我们编写的C语言程序是valid.ec,一要包含Informix提供的ctools.h和UNIX系统提供
的sys/types.h两个文件,二要定义输出的函数,其中结构userfuncs的格式是固定的,第
一个变量是格式文件要引用的函数名,当然要首先在格式文件的Define节中定义,第二个
变量是C语言程序中实际使用的函数名,并要以“0,0”结束。
valueptr valid(),abort(),mv_file(),get_date();
struct ufunc userfuncs[]={
"validate",valid,
"abort",abort,
"mv_file",mv_file,
"get_date",get_date,
0,0 };
3.1.2 编写函数
① 函数mv_file调用mv命令将文件移到某一目录。由格式文件传进来的两个参数u和v,
实际的值是u->;charp和v->;charp所指向的字符串。如果传进来的数据是整数就要用u->;int
获得,具体见文献[1]。
valueptr mv_file(u,v)
valueptr u,v;
{
char fs[90];
sprintf(fs,"mv %s /usr/icbacct/tmp/%s/%s",
v->;v_charp,u->;v_charp,v->;v_charp);
system(fs);
}
② 函数get_date获得系统当前的日期并以中文格式返回,返回字符串用strreturn函数,
如果是返回整数则要使用intreturn函数。
valueptr get_date(){
char s_date[15];struct tm *tm1;long ltm,time();
ltm=time(0L); tm1=localtime(<m);
sprintf(s_date,"%04d年%02d月%02d日",
tm1->;tm_year+1900,tm1->;tm_mon+1,tm1->;tm_mday);
strreturn(s_date,strlen(s_date));
}
③ 函数abort比较简单只是调用exit退出newacego。
valueptr abort(){
exit(0);
}
④ 函数valid较复杂,要涉及到文本文件的读写操作。我们将县支行管辖的四个交换
号逐一登记在subbrch.txt文件中。如果交换号在登记之列就返回0,否则返回1。
valueptr valid(user)
valueptr user;
{
char fuser[9];int eof=2;FILE *fp;
fp=fopen("/usr/icbacct/data/subbrch.txt","r" ;
while(1){
if(fscanf(fp,"%s\n",fuser)==EOF){
printf("交换号%s不在管辖范围!\n",user->;v_charp);
intreturn(1);
}
if(strcmp(fuser,user->;v_charp)==0) intreturn(0);
}
}
3.2 编译C语言程序
用/usr/informix/bin/cace编译C语言程序生成sacego新的定制版本。不过在INFORMIX
OnLine 5.0下,要把cace中的ESQLC和exec这两行改为如下内容才能顺利编译成功。
① ESQLC=${INFORMIXDIR=/usr/informix}/lib/esql/esqlc
② $ECHO exec cc -I$INFDIR/incl -I$INFDIR/incl/esql
$A $INFDIR/lib/libsace.a $LIB -lcurses
编译命令是:cace -s -o newacego valid.ec,生成sacego的新版本newacego。newacego的
参数格式与sacego一样。
3.3 生成报表
在单台机器上生成报表时运行: newacego -q fxt。如果是在客户服务器结构下,应在
客户机上运行:newacego -q -d icbdb@acct_qz fxt,表明目的数据库icbdb建立在服务器
上(acct_qz是服务器的主机名)。其中“-q”表示运行时不显示系统信息,“-d”用来指明
数据库。fxt是saceprep编译后生成的fxt.arc文件,运行时可省去后缀arc。
参 考 文 献
1 李春葆,马玉枫. UNIX环境中的高级数据处理技术. 北京:海洋出版社, 1993
2 希望公司. INFORMIX ESQL/C程序员使用手册. 北京:希望公司, 1994
附:
1,Title:The Report Tools of INFORMIX and its C Language Interface.
Author:Zhuang Wenxiang
2,dkqx.txt一例:
中国工商银行惠安县支行0225科目企业欠息统计表(1998年01月22日)
================================================================================
序号 存款帐号 存款户户名 表内欠息 表外欠息
================================================================================
1 111 02250153021 惠安二厂门市部 18,201.44 701.39
2 111 02250169186 土特产公司 10,000.00 .00
3 111 02250181221 批发部 .00 12,000.00
4 111 02250209785 图兴书店 79,144.58 1,799.56
5 111 02250212881 惠安科技实业有限公司 .00 45,282.84
================================================================================
页号: 1 户数: 5 合计: 107,346.02 59,783.79
中国工商银行惠安县支行0242科目企业欠息统计表(1998年01月22日)
================================================================================
序号 存款帐号 存款户户名 表内欠息 表外欠息
================================================================================
1 111 02420117259 泉州市织造实业有限公司 245,741.80 332,900.54
2 111 02420143418 鞋业有限公司 12,280.90 210.03
================================================================================
页号: 1 户数: 2 合计: 258,022.70 333,110.57
户数: 7 表内: 365,368.72 表外: 392,894.36 总计: 758,263.08 |
|