UML软件工程组织

基于VB-CrystalReport 的C/S报表架构讨论

作者:不详  出处: 网络

在这里我主要想和大家讨论一下Crystal Report的总体框架模式方面的问题,重点是如何使用VB有效的融合报表和数据,快速的进行报表开发工作。希望抛砖引玉,找到几种最好最快的开发模式。

本文暂不涉及技术细节,所以您可能觉得太过简单。但这样的问题的确是我们开发中的第一号大问题。在以后的讨论中,我们可以针对具体的问题讨论具体的技术细节。

 我准备了一份源代码,已经发给了部分网友,但是没有做详细的测试。我准备在明后天做完测试,如果各位有需要的话,应该在星期一左右可以发给你们(唉,星期六星期天还要加班!)。

 注意:有需要的朋友请跟帖说明,不要只给我发短消息。
 缩写语说明
 RPT Crystal Report Template File. 使用Crystal Report设计器生成的报表样式和模板文件,后缀名为RPT
 RDC Crystal Report Designer Component. Crystal Report的对象模型,用于在VB中操作Crystal Report.
 CRV Crystal Report Viewer. OCX 控件,用于在VB中提供报表预览界面。
 CRC Crystal Report Control. OCX 控件,在Crystal Report 8.0以前用于在VB中提供报表预览界面,现已被CRV取代。
 CDO Crystal Data Source Object. Crystal Report 提供的数据源对象,用于创建与数据库无关的动态数据源。

一,如何动态获取数据,生成报表?

 报表的一个主要特征就是根据事先设计好的模板样式和运行时的动态数据生成一份可查看与打印的文档。

 一般来说,VB的程序中整个报表流程中的主线,由它来对Crystal Report及其它工具发号施令,实现用户需求。

 VB是通过RDC模型来操作CrystalReport的。通常有如下两种模式生成报表:

 1,VB-RPT-DB

 VB调用RPT文件,RPT文件查询数据库得到数据,生成报表。此方法的详细流程如下:

 1)在Crystal Report 中设计好RPT文件,设计时可在CrystalReport中连接到数据库,得到查询数据,即时预览报表。
 2) 在VB中,通过RDC模型打开RPT文件,并将运行时和数据库连接所需的信息,如数据库名,用户,密码等传给RPT文件,如果RPT文件中有参数,还要传入参数。
 3)RPT文件连接到数据库,执行查询得到数据,生成报表。
 4) 在VB中,通过CRV控件打开报表进行预览或通过RDC模型进行打印,导出等操作。

 2,VB-DB-RPT

 VB先查询数据库得到结果集,再打开RPT文件并传入结果集,生成报表。此方法详细流程如下:

 1)在Crystal Report 中设计好RPT文件。由于此时结果集还未生成,只能使用TTX文件做为虚拟数据源,以便读取字段信息。所以在设计RPT文件之前,还要根据最终的结果集结构生成一个TTX文件。
 2)在VB中,连接到数据库,执行查询,得到结果集。
 3)在VB中,通过RDC模型打开RPT文件,传入结果集和参数,生成报表。
 4)在VB中,通过CRV控件打开报表进行预览或通过RDC模型进行打印,导出等操作。

显然,VB-RPT-DB模式远优于VB-DB-RPT模式,这体现在如下几个方面:

 1,设计时,VB-DB-RPT模式一般要求生成一个和最终结果集字段结构完全一致的TTX文件做为虚拟数据源,增大的开发和维护的工作量,而且还不能即时预览生成的报表。VB-RPT-DB模式则是直接和数据库相连,无需TTX文件,还能看到最终数据。
 2,运行时,VB-DB-RPT模式在得到结果集后,需要在VB中建立相应的结果集对象,并将其传入RPT文件,与VB-RPT-DB模式相比多了一个很大的对象--结果集对象。
 3,运行时,VB-DB-RPT模式的结果集从DB传到VB,再传到RPT, 而VB-RPT-DB模式的结果集直接从DB传到RPT,少了一次大的数据传递。
 4, 开发时,VB-DB-RPT模式一般每加一个报表,就要加两段代码:查询结果集和生成报表。而VB-RPT-DB模式可以很方便的实现一个接口应对所有的报表,新加报表几乎无需增加VB代码。二者的代码量不可同日而语。

二、如何生成报表模板文件?

 设计报表格式是所有报表工作的第一步。在CrystalReport中,有两个地方可以设计报表,Crystal Report集成设计器和内嵌在VB中的报表设计器。我个人觉得,内嵌报表设计器的做法弊远大于利,不推荐使用。

三,如何查询DB,得到运行时数据。

 通过VB查询DB得到结果集的方式有很多种,大家都很熟悉。通过RPT查询DB的方式主要有:存储过程,视图,表, Crystal Query, 直接SQL查询。

 其中最简单的莫过于直接连接到表,进行表查询。可是一般的报表都不会只涉及到一个表。在RPT文件中进行多表查询并设置查询条件的方法虽然简单,但是分散了整体的查询逻辑,而且操作烦琐,不是一个好办法。视图和存储过程是最好的选择,不过视图在性能上不及存储过程,所以我推荐使用存储过程。

 存储过程和视图是放在数据库中的。如果不允许或不能在数据库中建立视图和存储过程等,还可以利用Crystal Report自已的SQL查询功能,将完整的SQL查询语句写在RPT文件中。

 从Crystal Report自己的说明来看,Crystal Query文件就像是Crystal Report自己的存储过程。不过这个工具不包含在Crystal Report 9中。Crystal Report 官方网站提供了单独的免费下载。这个工具有两个限制:在Crystal Queryk中的每个字段,字段值长度不能超过20个字符, 总字段数据也不能超过20个。我是从Crystal Report的帮助文件中看到这些信息的。如是真的话,Crystal Query就没有什么竞争力了。
Crystal Report 也支持在设计RPT文件时直接写SQL,还可以带参数。这是个不错的功能。不知道是不是也有上述两个限制。

 我没有仔细测试Crystal Report内嵌SQL查询的功能,在此不宜多加评论。但我强烈推荐使用数据库的存储过程和视图。它有效的分离的报表工作中的逻辑和设计工作,利于分工合作,也利于编护和升级。
  
 使用Oracle的存储过程有如下限制,SQL Server 类似。

  • 要使用本地连接或ODBC连接到Oracle,不能使用OLE DB连接。建议使用本地连接。
  • 存储过程必须放到一个包内。结果集游标必须在包头声明。
  • 存储过程只能有输入参数。
  • 存储过程只能返回一个结果集,以输入输出型(IN OUT)的游标参数形式返回。
  • 结果集中的字段名称和类型通过打开游标的SQL语句直接确定。所以不能使用动态SQL,也不能通过嵌套调用存储过程的方式来将数据装入游标。

四,如何传递多个数据源和参数

 如果所有的数据源都是VB-RPT-DB模式,那么VB要做的连接工作就很简单,将一个动态建立的连接传给RDC就可以了。RDC再将连接传给每一个RPT中存在的数据源。即便在报表包含子报表时也是如此的简单。

 如果数据源是VB-DB-RPT模式,或都是好几种类型混合的。最好在设计报表的时候给每个数据源设定一个唯一名字(包括子报表的数据源),RDC就跟据名字来给每个数据源传递连接信息或结果集。

 事实上,在一个报表中即便存在多个数据源,由于Crystal Report要求互相之间要有主键的对应关系,而不能一个数据源处理完后再处理另一个数据源,所以这些多个的数据源在本质上仍然是一个数据源。我做过很多复杂的报表,很少碰到要用到多个数据源的。使用子报表的情况倒是占了一半左右。

 报表中的所参数都可以从VB中传入,建议也为所有的参数(包括子报表的参数)设定一个唯一的名字,由RDC跟据名字来传值,这样就不用理会报表中有哪些子报表了。

 在处理大量的报表时,将报表的RPT文件路径及其参数信息存入数据库,就可以实现新增报表无须修改VB程序。一般而言,一个系统中的绝大部分报表,其参数都差不多,很容易控制。

2.1 准备一个通用模板。

 一个项目中的所有报表,通常有一个统一的格式和标准。准备一个带有最基本格式和标准的RPT文件作为模板是个聪明的做法。不过Crystal Report中的模板向导功能并不是很强,不如直接创建一个RPT文件,然后复制来得快。

2.2 在Crystal Report 编辑环境中设定字段和数据的缺省模式

 在Crystal Report的选项菜单(文件->选项)中设定要不同字段,不同数据类型的显示格式和字体,这一点非常重要。它可以最大程度的保证的模板设计完全符合标准。减少重复劳动。

 在文件->报表选项中也有三项很重要的设置:

 1) 将数据库的NULL值转为默认值, 选中。
 2) 将其它NULL值转为默认值, 选中。
 3) 若无记录则取消打印, 不选。

 2.3 选择一个可以同时支持中英文的字体

 字体的选择直接影响版面效果。有的字体不支持中文,显示中文时就会出现乱码。有的中文字体在纯英文下不能使用。有的字体在Windows 2000/XP的中英文环境中大小不同。有的字体不是Windows 自带字体。

 最好在各种环境下试过后再决定采用哪种字体。

2.4 使用公式字段,汇总字段,运行时总计字段,以及变量实现数据的页面逻辑。

 Crystal Report中的公式其实就是一个表达式,它通过使用Crystal Report中的函数和操作符来操作数据源中的数据以得到最终想要显示的字段。在公式中还可以使用变量。

 在Crystal Report中有两种比较特别的公式:汇总公式和运行时总计公式。这两种公式都可以在公式编辑器(CR称之为公式工作室)中编辑,但比较复杂。CR提供了更简单的方法:在报表中选中要统计的字段,点出右键菜单,选中"插入…"项,就会弹出两个子菜单项:汇总,运行总计。

 汇总字段用于对特定的字段进行按组或全局的汇总统计。

 运行时总计字段在汇总字段的基础上加入条伯控制,使得在报表中跟据一个字段的不同取值分别得到另一个字段的总计数据成为可能。
在公式编辑器中可以使用变量。按作用域分,有局部变量,全局变量和共享变量。局部变量只在一个公式或函数中有效。全局变量在整个报表(但不包含子报表)中有效,共享变量在一个报表文件,包括子报表中都有效。

 2.5 使用子报表。

 跟据以往的项目经验,有一半左右的报表要用到子报表。在Crystal Report中,一个报表事实上只能处理一个结果集。存在多个结果集,要求互相之间有主键的对应关系,在本质上仍然是一个。如果报表要显示多个部分,就必须使用子报表。除没有单独的页眉和页脚外,子报表具有完整报表的所有特性。

 页面显示的时候,子报表的宽度会受到其在主报表中的宽度限制,所以在把子报表放入主报表的时候,一定要给予足够的宽度。

 主报表和子报表之间可以通过字段-参数的关系进行链接。即将主报表中的字段作为子报表的参数。

 2.6 使用交叉表。

 设有A,B两个字段,要求在报表中将A的全部值显示在第一列的列头,将B的全部值显示在每一行的行首,在行和列的主体部分显示出跟据相应的A值和B值计算出来的结果,如下图所示。这就是一个交叉表。

 交叉表中Crystal Report 中的一个对象,可以很简单的创建,注意交叉表应放到报表的报表页脚部分(Report Footer),不能放在细节部分或页眉。否则会导致重复显示和数据不全。

 Crystal Report对交叉表的分页功能支持不佳,所以要尽可能的不让数据超过一页。

2.7 使用分栏表。

 所谓分栏表,就是把页面分成几栏来显示结果。这样就可以在同一行显示多条记录。

 在节专家中选中详细资料节,注意只能选详细资料节的总节,不能选子节,就可以发出多栏格式化的选项,选中后界面上会多出一个布局的选项卡。在这个选项卡中就可以设置分栏。

2.8 最好不要在每页显示总页数。

 显示总页数会导致报表多一次全程运算,严重降低性能,最好不要在每页都显示总页数。

2.9 把报表打印出来检查格式与数据。

 这是个要诀,不打印出来检查,你的格式与数据很难保证没有错误。

2.10 设置字段或小节的条件显示条件

 Crystal Report中的所有对象,大到一个小节,小到一个字段,都可限定显示的格式。在这些对象的格式化编辑器中,有个抑制显示的选项,其右有一个公式编辑按钮,点击这个按钮就可以进入公式编辑器,编辑一个逻辑表达式来控制抑制显示的条件。注意,只有勾上抑制显示的选项,条件公式才会起作用。

 2.11 对字段进行有条件的格式化


 如果碰到类似这样要求,就要对字段进行有条件的格式化:将所有值大于1000的A字段加下划线显示。
 对字段的有条件格式化只能在公式编辑器(公式工作室)中实现。

 2.12 在导出为RTF是要注意字段对齐。

 如果报表的字段是随意放置的,在导出RTF文件是将会出现位置的严重错乱。


版权所有:UML软件工程组织